Python의 동시성 측면의 차이점
Python 3의 릴리스와 함께 비동기 작업 및 동시성에 관한 많은 새로운 추세를 듣고 있다는 점을 감안할 때 Python이 이러한 개념이나 기능을 방금 도입했다고 상상할 수 있습니다.
많은 초보자는 asyncio
를 사용하는 것이 동시 및 비동기 활동을 수행하는 유일한 실용적인 접근 방식이라고 생각할 수 있습니다. 이 기사에서는 Python에서 동시성을 달성하는 방법과 이점 또는 단점에 대해 설명합니다.
스레드 및 멀티스레딩
스레드는 아주 오랫동안 파이썬에 있었습니다. 결과적으로 스레드 덕분에 한 번에 여러 작업을 수행할 수 있습니다.
유감스럽게도 전형적인 메인라인 Python 버전인 CPython
은 여전히 글로벌 인터프리터 잠금(GIL
)을 사용합니다. 이는 오늘날 병렬 처리를 구현하는 일반적인 방법인 다중 스레드 응용 프로그램을 이상적이지 않게 만듭니다.
Python은 GIL
을 도입하여 CPython의 메모리 처리를 C와 보다 관리하기 쉽게 통합(예: 확장)할 수 있도록 했습니다.
GIL
은 Python 인터프리터가 동시에 하나의 스레드만 실행한다는 점에서 잠금 메커니즘입니다. Python의 byte
코드는 한 스레드에서만 동시에 실행할 수 있습니다.
예제 코드:
import threading
import time
import random
def worker(num):
sec = random.randrange(1, 5)
time.sleep(sec)
print("I am thread {}, who slept for {} seconds.".format(num, sec))
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
t.start()
print("Completed!")
출력:
Completed!
I am thread 1, who slept for 3 seconds.
I am thread 3, who slept for 2 seconds.
I am thread 4, who slept for 4 seconds.
프로세스 및 다중 처리
다중 처리는 많은 CPU를 사용합니다. 각 CPU가 병렬로 작동하기 때문에 여러 작업을 동시에 효과적으로 수행할 수 있습니다. CPU 바운드 작업의 경우 멀티프로세싱을 사용해야 합니다.
Python은 병렬화를 달성하기 위해 multiprocessing
모듈을 도입했습니다. 스레딩을 사용한 경우 매우 유사하게 느껴질 것입니다.
예제 코드:
import multiprocessing
import time
import random
def worker(num):
sec = random.randrange(1, 5)
time.sleep(sec)
print("I am process {}, who slept for {} seconds.".format(num, sec))
for i in range(3):
t = multiprocessing.Process(target=worker, args=(i,))
t.start()
print("Completed")
출력:
Completed
I am process 1, who slept for 1 seconds.
I am process 2, who slept for 2 seconds.
I am process 0, who slept for 3 seconds.
멀티스레딩 대신 CPU의 서로 다른 코어에서 실행되는 여러 프로세스를 사용하므로 Python 스크립트가 더 빨라집니다.
비동기 및 asyncio
동기식 작업에서는 작업이 차례로 동기화되어 수행됩니다. 그러나 작업은 서로 완전히 독립적인 비동기 작업으로 시작될 수 있습니다.
하나의 비동기
작업이 시작되고 실행이 다른 활동으로 전환되는 동안 계속 실행될 수 있습니다. 반면에 비동기 작업은 종종 백그라운드에서 실행되며 차단되지 않습니다(구현이 완료될 때까지 기다리게 함).
다른 유용한 기능과 함께 asyncio
는 이벤트 루프를 제공합니다. 이벤트 루프는 다양한 I/O 이벤트를 모니터링하고 준비된 작업으로 전환하며 I/O를 기다리는 작업을 일시 중지합니다.
결과적으로 우리는 미완성 프로젝트에 시간을 낭비하지 않습니다.
예제 코드:
import asyncio
import datetime
import random
async def my_sleep_func():
await asyncio.sleep(random.randint(0, 5))
async def displayDate(num, loop):
endTime = loop.time() + 60.0
while True:
print("Loop: {} Time: {}".format(num, datetime.datetime.now()))
if (loop.time() + 1.0) >= endTime:
break
await my_sleep_func()
loop = asyncio.get_event_loop()
asyncio.ensure_future(displayDate(1, loop))
asyncio.ensure_future(displayDate(2, loop))
loop.run_forever()
위의 코드 스니펫을 살펴보면 다음과 같습니다.
- 숫자와 이벤트 루프를 매개변수로 사용하는
비동기
함수displayDate
가 있습니다. - 해당 함수는 60초 후에 정지하는 무한 루프를 가지고 있습니다. 하지만 이 60초 동안 시간을 출력하고 낮잠을 반복한다.
await
기능은 다른async
기능이 완료될 때까지 기다릴 수 있습니다.- 함수를 이벤트 루프에 전달합니다(
ensure_future
함수 사용). - 이벤트 루프 실행을 시작합니다.
await
호출이 이루어질 때마다 asyncio
는 함수에 약간의 시간이 필요할 것임을 이해합니다. asyncio
가 정지된 함수의 I/O가 준비되었음을 확인하면 프로세스를 다시 시작합니다.
이제 요점은 세 가지 형태의 동시성 중 무엇을 사용해야 하느냐입니다. 의사 결정에 도움이 되도록 다음 사항을 참고할 수 있습니다.
- CPU 바운드 작업에 다중 처리를 사용합니다.
- I/O Bound, Fast I/O 및 제한된 수의 연결에 멀티스레딩을 사용합니다.
- I/O Bound, Slow I/O 및 많은 연결에 대해 비동기 IO를 사용합니다.
asyncio/await
는 Python 3.5 이상에서 작동합니다.
아래 의사 코드를 참조할 수도 있습니다.
if io_bound:
if io_very_slow:
print("Use asyncio")
else:
print("Use multithreading")
else:
print("multiprocessing")
Marion specializes in anything Microsoft-related and always tries to work and apply code in an IT infrastructure.
LinkedIn