본문 바로가기
자습

Multithreading VS Multiprocessing in Python

by litaro 2019. 11. 3.

https://medium.com/contentsquare-engineering-blog/multithreading-vs-multiprocessing-in-python-ece023ad55a

 

Multithreading VS Multiprocessing in Python

Revealing the true face of Multithreading

medium.com

Python에서 Thread를 이용해서 timeout을 걸어서 하던 작업을 멈추고 싶었다. 

Python은 기본적으로 single thread에서 실행되고 순차적으로 실행되어서 직관?적이라서 구현하기 용이한 언어라고 생각했는데 의도치않게? thread를 써야하는 요구사항이 생긴것이다. 

그러다가 찾은 글.. 나도 잘 몰라서 thread 나 process나 하는 기능은 동일해서 뭘써야 하나 하던 중에 찾은 블로그

Thread

일단 Thread는 threading module에서 Thread Objects 를 가져와서 start() 함수를 호출하면 해당 Thread의 run()이 호출되면서 실행이 된다.

https://docs.python.org/3/library/threading.html

 

threading — Thread-based parallelism — Python 3.8.0 documentation

threading — Thread-based parallelism Source code: Lib/threading.py This module constructs higher-level threading interfaces on top of the lower level _thread module. See also the queue module. Changed in version 3.7: This module used to be optional, it is

docs.python.org

import time
from threading import Thread, Event

# 다른 Thread 에 signal 을 보내기 위한 Event Object
stop_event = Event()


def do_actions(start_value):
    i = start_value
    while True:
        i += 1
        print(i)
        time.sleep(1)

        # 다른 Thread 에서 stop 하라는 signal 이 왔는지 확
        if stop_event.is_set():
            break


if __name__ == '__main__':
    # Thread 생
    action_thread = Thread(target=do_actions, args=(0,))

    # Thread 시작하고 10 초의 time out 시간까지는 현재 Thread Block
    action_thread.start()
    action_thread.join(timeout=10)

    # 다른 Thread 에 Stop 하라는 event 보내기
    stop_event.set()
    print("timed out! ")
1
2
3
4
5
6
7
8
9
10
timed out! 

Process finished with exit code 0

Process

Process는 multiprocessing module 에서 Process Object를 가져와서 start()함수를 호출하는 것으로 Thread와 유사한 API 사용법을 가진다.

import time
from multiprocessing import Process, Event

# 다른 Process 에 signal 을 보내기 위한 Event Object
stop_event = Event()


def do_actions(start_value):
    i = start_value
    while True:
        i += 1
        print(i)
        time.sleep(1)

        # 다른 Process 에서 stop 하라는 signal 이 왔는지 확
        if stop_event.is_set():
            break


if __name__ == '__main__':
    # We create a Process
    process = Process(target=do_actions, args=(0,))

    # process 시작하고 10 초의 time out 시간까지는 현재 process Block

    process.start()
    process.join(timeout=10)

    # 다른 process 에 Stop 하라는 event 보내
    stop_event.set()
    print("timed out! ")
1
2
3
4
5
6
7
8
9
10
timed out! 

Process finished with exit code 0

그러다 보니 나같이 잘 모르는 사람은 Thread나 Process 나 동일하네... 하며 뭐를 쓸지 큰 고민없이 그냥 느낌 가는데로 사용할 것이다.

하지만 위와 같이 단순하게 하나의 Process와 Thread를 쓰는 예제야 드러나지 않지만 MultiThreading과 MultiProcessing은 다르다.

아래와 같이 동일하게 2개의 Thread와 2개의 Precess로 계산을 하면 걸리는 시간이 각각 12초, 6초 이다.

왜 Thread는 시간이 이렇게 많이 걸리는지...

Thread 2개

import time
from threading import Thread, Event

# 다른 Thread 에 signal 을 보내기 위한 Event Object
stop_event = Event()


def do_actions():
    count = 0
    for i in range(10 ** 8):
        count += i


if __name__ == '__main__':
    # Thread 생성
    action_thread1 = Thread(target=do_actions, args=())
    action_thread2 = Thread(target=do_actions, args=())

    start_time = time.time()

    # Thread 시작
    action_thread1.start()
    action_thread2.start()

    action_thread1.join()
    action_thread2.join()

    end_time = time.time()

    print(f"spent time : {end_time - start_time}")
spent time : 12.77131700515747

Process finished with exit code 0

Process 2개

import time
from multiprocessing import Process


def do_actions():
    count = 0
    for i in range(10 ** 8):
        count += i


if __name__ == '__main__':
    # We create a Process
    process1 = Process(target=do_actions, args=())
    process2 = Process(target=do_actions, args=())

    start_time = time.time()

    # Process 시작
    process1.start()
    process2.start()
    process1.join()
    process2.join()

    end_time = time.time()

    print(f"spent time : {end_time - start_time}")
spent time : 6.51689338684082

Process finished with exit code 0

그건 바로 GIL (Global Interpreter Lock) 때문이란다.

메모리 관리를 위해 Python에서는 한번에 하나의 Thread만 Resource를 컨트롤하도록 한것이다.

즉, Thread를 사용하면 GIL로 인해 하나의 Process (공유하는 memory space) 에서 돌아가는 Thread이기 때문에 동시에 두개를 할수 없다.

반면, Process를 사용하면 여러 프로세스 (별도의 memory space) 를 사용하기 때문에 GIL 의 제한을 받지 않는다. 

요약

하나의 python process 에서는 하나의 Thread만 돌아간다.

MultiProcessing은 parallelism 에 효과적이고, MultiThreading은 concurrency가 중요한 일에 효과적이다.

MultiProcessing은 CPU를 heavy하게 쓰는 일 (계산..)에 좋고, MultiThreading은 database query나 webpage load와 같은 일에 좋다.

'자습' 카테고리의 다른 글

JWT in the modern web  (0) 2019.11.22
How to Host a Static Website with S3, CloudFront and Route53  (0) 2019.11.08
Flask, Flask-RESTPlus, and Swagger UI  (0) 2019.10.09
Django vs. Flask  (1) 2019.09.28
click을 이용한 python CLI  (0) 2019.09.08