동시성 프로그래밍은 여러 작업을 동시에 실행하는 프로그래밍 접근 방식입니다. Python에서 asyncio는 비동기 프로그래밍을 구현하는 강력한 도구입니다. 코루틴 개념을 기반으로 asyncio는 I/O 집약적인 작업을 효율적으로 처리할 수 있습니다. 이번 글에서는 asyncio의 기본 원리와 사용법을 소개하겠습니다.
우리는 I/O 작업을 처리할 때 멀티스레딩을 사용하면 일반 단일 스레드에 비해 효율성이 크게 향상될 수 있다는 것을 알고 있습니다. 그렇다면 왜 여전히 asyncio가 필요한가요?
멀티스레딩에는 많은 장점이 있고 널리 사용되지만 다음과 같은 몇 가지 제한 사항도 있습니다.
이러한 문제를 해결하기 위해 Asyncio가 등장했습니다.
먼저 Sync(동기)와 Async(비동기)의 개념을 구분해 보겠습니다.
요약하자면, asyncio의 작동 원리는 코루틴과 이벤트 루프의 메커니즘을 기반으로 합니다. 비동기 작업에 코루틴을 사용하고 코루틴의 예약 및 실행을 담당하는 이벤트 루프를 가짐으로써 asyncio는 효율적인 비동기 프로그래밍 모델을 실현합니다.
코루틴은 asyncio에서 중요한 개념입니다. 스레드 전환의 오버헤드 없이 작업 간을 빠르게 전환할 수 있는 경량 실행 단위입니다. 코루틴은 async 키워드로 정의할 수 있으며, wait 키워드는 코루틴의 실행을 일시 중지하고 특정 작업이 완료된 후 다시 시작하는 데 사용됩니다.
다음은 비동기 프로그래밍에 코루틴을 사용하는 방법을 보여주는 간단한 샘플 코드입니다.
import asyncio async def hello(): print("Hello") await asyncio.sleep(1) # Simulate a time-consuming operation print("World") # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(hello())
이 예에서 hello() 함수는 async 키워드로 정의된 코루틴입니다. 코루틴 내에서 Wait를 사용하여 실행을 일시 중지할 수 있습니다. 여기서 asyncio.sleep(1)은 시간이 많이 걸리는 작업을 시뮬레이션하는 데 사용됩니다. run_until_complete() 메서드는 이벤트 루프에 코루틴을 추가하고 실행합니다.
asyncio는 주로 네트워크 요청, 파일 읽기 및 쓰기와 같은 I/O 집약적인 작업을 처리하는 데 사용됩니다. 이는 비동기 프로그래밍을 쉽게 달성하기 위해 wait 키워드와 함께 사용할 수 있는 비동기 I/O 작업을 위한 일련의 API를 제공합니다.
다음은 비동기 네트워크 요청에 asyncio를 사용하는 방법을 보여주는 간단한 샘플 코드입니다.
import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(): async with aiohttp.ClientSession() as session: html = await fetch(session, 'https://www.example.com') print(html) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
이 예에서는 네트워크 요청에 aiohttp 라이브러리를 사용합니다. fetch() 함수는 코루틴입니다. session.get() 메서드를 통해 비동기 GET 요청을 시작하고 wait 키워드를 사용하여 응답이 반환될 때까지 기다립니다. main() 함수는 또 다른 코루틴입니다. 재사용을 위해 내부에 ClientSession 개체를 생성한 다음 fetch() 메서드를 호출하여 웹 페이지 콘텐츠를 가져와 인쇄합니다.
참고: 여기서는 요청 라이브러리가 asyncio와 호환되지 않지만 aiohttp 라이브러리는 호환되기 때문에 요청 라이브러리 대신 aiohttp를 사용합니다. asyncio를 효과적으로 활용하려면, 특히 강력한 기능을 발휘하려면 많은 경우 해당 Python 라이브러리가 필요합니다.
asyncio는 asyncio.gather() 및 asyncio.wait()와 같이 여러 작업을 동시에 실행하기 위한 몇 가지 메커니즘도 제공합니다. 다음은 이러한 메커니즘을 사용하여 여러 코루틴 작업을 동시에 실행하는 방법을 보여주는 샘플 코드입니다.
import asyncio async def task1(): print("Task 1 started") await asyncio.sleep(1) print("Task 1 finished") async def task2(): print("Task 2 started") await asyncio.sleep(2) print("Task 2 finished") async def main(): await asyncio.gather(task1(), task2()) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
이 예에서는 시간이 많이 걸리는 작업을 수행하는 두 개의 코루틴 작업 task1() 및 task2()를 정의합니다. 코루틴 main()은 asyncio.gather()를 통해 이 두 작업을 동시에 시작하고 완료될 때까지 기다립니다. 동시 실행은 프로그램 실행 효율성을 향상시킬 수 있습니다.
실제 프로젝트에서는 멀티스레딩을 선택해야 할까요, 아니면 asyncio를 선택해야 할까요? 빅샷이 생생하게 요약해줬네요:
import asyncio async def hello(): print("Hello") await asyncio.sleep(1) # Simulate a time-consuming operation print("World") # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(hello())
목록을 입력하세요. 목록의 각 요소에 대해 0부터 이 요소까지 모든 정수의 제곱의 합을 계산하려고 합니다.
import asyncio import aiohttp async def fetch(session, url): async with session.get(url) as response: return await response.text() async def main(): async with aiohttp.ClientSession() as session: html = await fetch(session, 'https://www.example.com') print(html) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
실행시간은 계산 16.00943413000002초
import asyncio async def task1(): print("Task 1 started") await asyncio.sleep(1) print("Task 1 finished") async def task2(): print("Task 2 started") await asyncio.sleep(2) print("Task 2 finished") async def main(): await asyncio.gather(task1(), task2()) # Create an event loop loop = asyncio.get_event_loop() # Add the coroutine to the event loop and execute loop.run_until_complete(main())
실행시간은 7.314132894999999초로 계산됩니다
이 개선된 코드에서는 Concurrent.futures.ProcessPoolExecutor를 사용하여 프로세스 풀을 생성한 다음 executor.map() 메서드를 사용하여 작업을 제출하고 결과를 얻습니다. executor.map()을 사용한 후 결과를 가져와야 하는 경우 결과를 목록으로 반복하거나 다른 방법을 사용하여 결과를 처리할 수 있습니다.
if io_bound: if io_slow: print('Use Asyncio') else: print('Use multi-threading') elif cpu_bound: print('Use multi-processing')
실행시간은 5.024221667초로 계산됩니다
concurrent.futures.ProcessPoolExecutor와 다중 처리는 모두 Python에서 다중 프로세스 동시성을 구현하기 위한 라이브러리입니다. 몇 가지 차이점이 있습니다:
요약하자면, Concurrent.futures.ProcessPoolExecutor는 기본 다중 프로세스 기능을 캡슐화하는 상위 수준 인터페이스로, 간단한 다중 프로세스 작업 병렬화에 적합합니다. 멀티프로세싱은 더 낮은 수준의 라이브러리로, 더 많은 제어와 유연성을 제공하며, 프로세스를 세밀하게 제어해야 하는 시나리오에 적합합니다. 특정 요구 사항에 따라 적절한 라이브러리를 선택해야 합니다. 단순한 작업 병렬화라면 Concurrent.futures.ProcessPoolExecutor를 사용하여 코드를 단순화할 수 있습니다. 더 낮은 수준의 제어와 통신이 필요한 경우 다중 처리 라이브러리를 사용할 수 있습니다.
멀티스레딩과 달리 asyncio는 단일 스레드이지만 내부 이벤트 루프 메커니즘을 통해 여러 다른 작업을 동시에 실행할 수 있으며 멀티스레딩보다 자율 제어가 더 뛰어납니다.
asyncio의 작업은 작업 중에 중단되지 않으므로 경쟁 조건이 발생하지 않습니다.
특히 I/O 작업이 많은 시나리오에서는 asyncio가 멀티스레딩보다 작업 효율성이 더 높습니다. asyncio에서 작업 전환 비용은 스레드 전환 비용보다 훨씬 적고, asyncio가 시작할 수 있는 작업 수는 멀티스레딩의 스레드 수보다 훨씬 크기 때문입니다.
그러나 많은 경우 asyncio를 사용하려면 이전 예의 aiohttp와 같은 특정 타사 라이브러리의 지원이 필요하다는 점에 유의해야 합니다. 그리고 I/O 작업이 빠르고 무겁지 않다면 멀티스레딩을 사용하는 것도 효과적으로 문제를 해결할 수 있습니다.
마지막으로 Flask/FastAPI 배포에 이상적인 플랫폼인 Leapcell을 소개하겠습니다.
Leapcell은 최신 분산 애플리케이션을 위해 특별히 설계된 클라우드 컴퓨팅 플랫폼입니다. 종량제 가격 모델을 통해 유휴 비용이 발생하지 않으므로 사용자는 실제로 사용한 리소스에 대해서만 비용을 지불하면 됩니다.
문서에서 자세히 알아보세요!
Leapcell 트위터: https://x.com/LeapcellHQ
위 내용은 고성능 Python: Asyncio의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!