> 백엔드 개발 > 파이썬 튜토리얼 > 고성능 Python: Asyncio

고성능 Python: Asyncio

Susan Sarandon
풀어 주다: 2025-01-01 06:30:09
원래의
950명이 탐색했습니다.

High-Performance Python: Asyncio

동시성 프로그래밍은 여러 작업을 동시에 실행하는 프로그래밍 접근 방식입니다. Python에서 asyncio는 비동기 프로그래밍을 구현하는 강력한 도구입니다. 코루틴 개념을 기반으로 asyncio는 I/O 집약적인 작업을 효율적으로 처리할 수 있습니다. 이번 글에서는 asyncio의 기본 원리와 사용법을 소개하겠습니다.

High-Performance Python: Asyncio

비동기가 필요한 이유

우리는 I/O 작업을 처리할 때 멀티스레딩을 사용하면 일반 단일 스레드에 비해 효율성이 크게 향상될 수 있다는 것을 알고 있습니다. 그렇다면 왜 여전히 asyncio가 필요한가요?

멀티스레딩에는 많은 장점이 있고 널리 사용되지만 다음과 같은 몇 가지 제한 사항도 있습니다.

  • 예를 들어 멀티스레딩의 실행 프로세스가 쉽게 중단되어 경쟁 조건이 발생할 수 있습니다.
  • 게다가 스레드 전환 자체에는 일정한 비용이 발생하며 스레드 수를 무한정 늘릴 수는 없습니다. 따라서 I/O 작업이 매우 많으면 멀티스레딩이 고효율 및 고품질 요구 사항을 충족하지 못할 가능성이 높습니다.

이러한 문제를 해결하기 위해 Asyncio가 등장했습니다.

동기화 VS 비동기

먼저 Sync(동기)와 Async(비동기)의 개념을 구분해 보겠습니다.

  • 동기화는 작업이 차례로 실행되는 것을 의미합니다. 다음 작업은 이전 작업이 완료된 후에만 실행할 수 있습니다.
  • 비동기란 서로 다른 작업을 교대로 실행할 수 있음을 의미합니다. 작업 중 하나가 차단되면 프로그램은 기다리지 않고 계속 실행 가능한 작업을 찾습니다.

비동기 작동 방식

  1. 코루틴: asyncio는 코루틴을 사용하여 비동기 작업을 수행합니다. 코루틴은 async 키워드로 정의된 특수 함수입니다. 코루틴에서는 wait 키워드를 사용하여 현재 코루틴의 실행을 일시 중지하고 비동기 작업이 완료될 때까지 기다릴 수 있습니다.
  2. 이벤트 루프: 이벤트 루프는 asyncio의 핵심 메커니즘 중 하나입니다. 코루틴을 예약 및 실행하고 코루틴 간 전환을 처리하는 역할을 담당합니다. 이벤트 루프는 실행 가능한 작업을 지속적으로 폴링합니다. 작업이 준비되면(예: I/O 작업이 완료되거나 타이머가 만료되는 경우) 이벤트 루프는 해당 작업을 실행 대기열에 넣고 다음 작업을 계속합니다.
  3. 비동기 작업: asyncio에서는 비동기 작업을 생성하여 코루틴을 실행합니다. 비동기 작업은 코루틴을 대기 가능한 객체로 캡슐화하고 처리를 위해 이벤트 루프에 제출하는 asyncio.create_task() 함수에 의해 생성됩니다.
  4. 비동기 I/O 작업: asyncio는 일련의 비동기 I/O 작업(예: 네트워크 요청, 파일 읽기 및 쓰기 등)을 제공합니다. 이는 코루틴 및 이벤트 루프와 원활하게 통합될 수 있습니다. 키워드를 기다립니다. 비동기 I/O 작업을 사용하면 I/O 완료를 기다리는 동안 차단이 방지되어 프로그램 성능과 동시성이 향상됩니다.
  5. 콜백: asyncio는 콜백 함수를 사용하여 비동기 작업의 결과를 처리하는 것도 지원합니다. asyncio.ensure_future() 함수를 사용하면 콜백 함수를 어웨이터블 객체로 캡슐화하고 처리를 위해 이벤트 루프에 제출할 수 있습니다.
  6. 동시 실행: asyncio는 여러 코루틴 작업을 동시에 실행할 수 있습니다. 이벤트 루프는 작업 준비 상태에 따라 코루틴 실행을 자동으로 예약하므로 효율적인 동시 프로그래밍이 가능합니다.

요약하자면, 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() 메서드는 이벤트 루프에 코루틴을 추가하고 실행합니다.

비동기 I/O 작업

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())
로그인 후 복사
로그인 후 복사
  • I/O 바인딩이고 I/O 작업이 느리고 많은 작업/스레드의 협력이 필요한 경우 asyncio를 사용하는 것이 더 적합합니다.
  • I/O 바인딩이지만 I/O 작업이 빠르고 제한된 수의 작업/스레드만 필요한 경우 멀티스레딩이 가능합니다.
  • CPU 바인딩된 경우 프로그램 실행 효율성을 높이기 위해 다중 처리가 필요합니다.

관행

목록을 입력하세요. 목록의 각 요소에 대해 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초

Concurrent.futures를 사용한 비동기 구현

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에서 다중 프로세스 동시성을 구현하기 위한 라이브러리입니다. 몇 가지 차이점이 있습니다:

  1. 인터페이스 기반 캡슐화: Concurrent.futures.ProcessPoolExecutor는 Concurrent.futures 모듈에서 제공하는 고급 인터페이스입니다. 기본 다중 프로세스 기능을 캡슐화하여 다중 프로세스 코드를 더 쉽게 작성할 수 있습니다. 멀티프로세싱은 Python의 표준 라이브러리 중 하나이지만 완전한 멀티프로세스 지원을 제공하고 프로세스에 대한 직접 작업을 허용합니다.
  2. API 사용법: Concurrent.futures.ProcessPoolExecutor의 사용법은 스레드 풀의 사용법과 유사합니다. 호출 가능한 객체(예: 함수)를 실행을 위해 프로세스 풀에 제출하고 실행 결과를 얻는 데 사용할 수 있는 Future 객체를 반환합니다. 멀티프로세싱은 보다 낮은 수준의 프로세스 관리 및 통신 인터페이스를 제공합니다. 프로세스는 명시적으로 생성, 시작 및 제어될 수 있으며 큐나 파이프를 사용하여 여러 프로세스 간의 통신을 수행할 수 있습니다.
  3. 확장성 및 유연성: 멀티프로세싱은 더 낮은 수준의 인터페이스를 제공하므로 Concurrent.futures.ProcessPoolExecutor에 비해 더 유연합니다. 프로세스를 직접 운영함으로써 프로세스 우선순위 설정, 프로세스 간 데이터 공유 등 각 프로세스에 대한 보다 세밀한 제어가 가능합니다. Concurrent.futures.ProcessPoolExecutor는 간단한 작업 병렬화에 더 적합하며, 많은 기본 세부 정보를 숨기고 다중 프로세스 코드를 더 쉽게 작성할 수 있습니다.
  4. 교차 플랫폼 지원: Concurrent.futures.ProcessPoolExecutor와 멀티프로세싱 모두 크로스 플랫폼 다중 프로세스 지원을 제공하며 다양한 운영 체제에서 사용할 수 있습니다.

요약하자면, Concurrent.futures.ProcessPoolExecutor는 기본 다중 프로세스 기능을 캡슐화하는 상위 수준 인터페이스로, 간단한 다중 프로세스 작업 병렬화에 적합합니다. 멀티프로세싱은 더 낮은 수준의 라이브러리로, 더 많은 제어와 유연성을 제공하며, 프로세스를 세밀하게 제어해야 하는 시나리오에 적합합니다. 특정 요구 사항에 따라 적절한 라이브러리를 선택해야 합니다. 단순한 작업 병렬화라면 Concurrent.futures.ProcessPoolExecutor를 사용하여 코드를 단순화할 수 있습니다. 더 낮은 수준의 제어와 통신이 필요한 경우 다중 처리 라이브러리를 사용할 수 있습니다.

요약

멀티스레딩과 달리 asyncio는 단일 스레드이지만 내부 이벤트 루프 메커니즘을 통해 여러 다른 작업을 동시에 실행할 수 있으며 멀티스레딩보다 자율 제어가 더 뛰어납니다.

asyncio의 작업은 작업 중에 중단되지 않으므로 경쟁 조건이 발생하지 않습니다.

특히 I/O 작업이 많은 시나리오에서는 asyncio가 멀티스레딩보다 작업 효율성이 더 높습니다. asyncio에서 작업 전환 비용은 스레드 전환 비용보다 훨씬 적고, asyncio가 시작할 수 있는 작업 수는 멀티스레딩의 스레드 수보다 훨씬 크기 때문입니다.

그러나 많은 경우 asyncio를 사용하려면 이전 예의 aiohttp와 같은 특정 타사 라이브러리의 지원이 필요하다는 점에 유의해야 합니다. 그리고 I/O 작업이 빠르고 무겁지 않다면 멀티스레딩을 사용하는 것도 효과적으로 문제를 해결할 수 있습니다.

  • asyncio는 비동기 프로그래밍을 구현하기 위한 Python 라이브러리입니다.
  • 코루틴은 asyncio의 핵심 개념으로, async 및 Wait 키워드를 통해 비동기 작업을 수행합니다.
  • asyncio는 비동기 I/O 작업을 위한 강력한 API를 제공하며 I/O 집약적인 작업을 쉽게 처리할 수 있습니다.
  • asyncio.gather()와 같은 메커니즘을 통해 여러 코루틴 작업을 동시에 실행할 수 있습니다.

Leapcell: FastAPI, Flask 및 기타 Python 애플리케이션을 위한 이상적인 플랫폼

마지막으로 Flask/FastAPI 배포에 이상적인 플랫폼인 Leapcell을 소개하겠습니다.

Leapcell은 최신 분산 애플리케이션을 위해 특별히 설계된 클라우드 컴퓨팅 플랫폼입니다. 종량제 가격 모델을 통해 유휴 비용이 발생하지 않으므로 사용자는 실제로 사용한 리소스에 대해서만 비용을 지불하면 됩니다.

High-Performance Python: Asyncio

  1. 다국어 지원
    • JavaScript, Python, Go 또는 Rust에서의 개발을 지원합니다.
  2. 무제한 프로젝트 무료 배포
    • 사용량에 따라 요금이 부과됩니다. 요청이 없을 시에는 요금이 부과되지 않습니다.
  3. 탁월한 비용 효율성
    • 유휴 수수료 없이 사용한 만큼만 지불하세요.
    • 예를 들어 25달러로 694만 개의 요청을 지원할 수 있으며 평균 응답 시간은 60밀리초입니다.
  4. 단순화된 개발자 경험
    • 손쉬운 설정을 위한 직관적인 사용자 인터페이스.
    • 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
    • 실행 가능한 통찰력을 제공하는 실시간 지표 및 로그.
  5. 손쉬운 확장성과 고성능
    • 높은 동시성을 쉽게 처리할 수 있는 자동 확장.
    • 운영 오버헤드가 없어 개발자가 개발에 집중할 수 있습니다.

문서에서 자세히 알아보세요!
Leapcell 트위터: https://x.com/LeapcellHQ

위 내용은 고성능 Python: Asyncio의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:dev.to
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿