Python/Concurrency

Python Multithreading and Multiprocessing - Multithreading

jinmc 2020. 10. 30. 15:18
반응형

www.youtube.com/watch?v=IEEhzQoKtQU

위는 영어로 된 동영상인데, 영어만 알아듣는다면 굉장히 좋다...

 

일단 동시성(Concurrency)와 병렬(Parellism)을 구분할 수 있어야 한다.

동시성은, Multithreading을 포함한 asyncronous 한 function들을 말한다. 하지만 암시적으로, 병렬이 아니라는 걸 말한다.

병렬은, 동시성을 포함하는 개념이며, 여러 코어에 프로세스가 돌아갈 수 있게 한다.

즉, 동시성은 한 코어에서 여러 개의 쓰레드가 돌아가는 걸 얘기하지만 

병렬은 여러개의 코어 각각에서 각각의 프로세스가 돌아가는 걸 얘기한다.

즉, 동시성은 실제로 cpu가 많이 소모되는 일을 할 때에는 많은 속도향상을 하진 못하고, 그걸 하려면 병렬 프로그래밍이 필요하다.

 

그럼 왜 동시성을 사용하느냐? 

- IO 관련된 일을 할 떄는 동시성이 중요! -> 렉에 걸려도 마우스는 움직여야되니깐.. 

 

그럼 병렬은 무조건 좋은거임?

- 딱히 그렇지도 않은게, 병렬 프로그래밍은 메모리를 share할 수 없음!

- 게다가, multi processing은 리소스가 많이 듬!

 

오케이.. 그럼 일단 코드를 좀 볼까?

보통 Python에서 multithreading (동시성) 은 threading.thread 를 많이 쓴다.

 

import time
import threading

start = time.perf_counter()

def something():
	print('sleeping..1 second)
    time.sleep(1)
    print('Done sleeping')
    
threads = []

for _ in range(10):
	t = threading.Thread(target=do_something)
    t.start()
    threads.append(t)
    
for thread in threads:
	thread.join()

finish = time.perf_counter()

print(f'Finished in {round(finish-start, 2} second')

좀 더 멋진 syntax로는 concurrent.futures.ThreadPoolExecutor가 있다.

 

import time
import concurrent.futures

start = time.perf_counter()

def something(s):
    print('sleeping..1 second)
    time.sleep(s)
    return 'done!'
    # print('Done sleeping')
    
with concurrent.futures.ThreadPoolExecutor() as executor:
    f1 = executor.submit(something, 1)
    f2 = executor.submit(something, 1)
    print(f1.result())
    print(f2.result())

finish = time.perf_counter()

print(f'Finished in {round(finish-start, 2} second')

 

좀 달라졌다. 

리턴 값도 받을 수 있고, 파라미터도 넘겨준다.

물론, 위 처럼 for loop를 사용할 수도 있다. 

좀 더 pythonic하게 짠다면, list_comprehension을 사용할 수 도 있다.

 

먼저 끝난 thread부터 결과값을 받아보고 싶다면, concurrent.futures.as_completed를 사용한다.

secs = [5, 4, 3, 2, 1]
results = [executor.submit(something, sec) for sec in secs]

for f in concurrent.futures.as_completed(results):
    print(f.result())

map을 사용하면, 서로 다른 parameter list를 function에 map 하는것도 가능하다.

results = executor.map(something, secs)

일단 그렇고, multiprocessing은 다음에 하는걸로.. 

반응형