Python 비동기 점검을위한 3 단계

Python은 비동기 프로그램을 작성하는 방법을 지원하는 많은 언어 중 하나입니다. 여러 작업간에 자유롭게 전환하는 프로그램, 모두 한 번에 실행되므로 어떤 작업도 다른 작업의 진행을 유지하지 못합니다.

하지만 여러분은 주로 동기식 Python 프로그램을 작성했을 것입니다. 한 번에 한 가지 작업 만 수행하고 다른 작업을 시작하기 전에 각 작업이 완료되기를 기다리는 프로그램입니다. 비동기로 이동하려면 새로운 구문뿐만 아니라 코드에 대한 새로운 사고 방식을 배워야하기 때문에 혼란 스러울 수 있습니다. 

이 기사에서는 기존의 동기 프로그램을 어떻게 비동기 프로그램으로 바꿀 수 있는지 살펴볼 것입니다. 여기에는 비동기 구문으로 함수를 장식하는 것 이상이 포함됩니다. 또한 프로그램이 실행되는 방식에 대해 다르게 생각하고 비동기가 그것이하는 일에 대한 좋은 은유인지 결정해야합니다. 

[추가 정보 : Serdar Yegulalp의 Smart Python 동영상에서 Python 팁과 요령 배우기]

Python에서 비동기를 사용하는 경우

Python 프로그램은 다음과 같은 특성이있을 때 비동기에 가장 적합합니다.

  • 대부분 I / O에 의해 제한되는 작업을하거나 장기 실행 네트워크 읽기와 같이 외부 프로세스가 완료 될 때까지 기다립니다.
  • 사용자 상호 작용을 처리하는 동시에 이러한 종류의 작업 중 하나 이상을 한 번에 수행하려고합니다.
  • 문제의 작업은 계산적으로 무겁지 않습니다.

스레딩을 사용하는 Python 프로그램은 일반적으로 비동기를 사용하기에 좋은 후보입니다. Python의 스레드는 협력 적입니다. 그들은 필요에 따라 서로에게 양보합니다. Python의 비동기 작업은 동일한 방식으로 작동합니다. 또한 비동기는 스레드에 비해 다음과 같은 이점을 제공합니다.

  • async/의 await구문은 쉽게 프로그램의 비동기 부분을 식별 할 수 있습니다. 대조적으로, 스레드에서 실행되는 앱 부분을 한눈에 파악하기 어려운 경우가 많습니다. 
  • 비동기 작업은 동일한 스레드를 공유하기 때문에 액세스하는 모든 데이터는 GIL (객체에 대한 액세스를 동기화하는 Python의 기본 메커니즘)에 의해 자동으로 관리됩니다. 스레드에는 종종 동기화를위한 복잡한 메커니즘이 필요합니다. 
  • 비동기 작업은 스레드보다 관리 및 취소가 더 쉽습니다.

Python 프로그램에 다음과 같은 특성이있는 경우 비동기를 사용 하지 않는 것이 좋습니다.

  • 작업은 계산 비용이 많이 듭니다. 예를 들어 많은 수를 처리합니다. 무거운 계산 작업은으로 가장 잘 처리 multiprocessing되므로 전체 하드웨어 스레드를 각 작업 에 할당 할 수 있습니다 .
  • 작업은 인터리브로 인한 이점이 없습니다. 각 작업이 마지막 작업에 의존하는 경우 작업을 비동기 적으로 실행할 필요가 없습니다. 즉, 프로그램이 일련 의 직렬 작업을 포함하는 경우  각 집합을 비동기 적으로 실행할 수 있습니다.

1 단계 : 프로그램의 동기 및 비동기 부분 식별

Python 비동기 코드는 Python 애플리케이션의 동기 부분에서 시작하고 관리해야합니다. 이를 위해 프로그램을 비동기로 변환 할 때 첫 번째 작업은 코드의 동기화 부분과 비동기 부분 사이에 선을 그리는 것입니다.

비동기에 대한 이전 기사에서는 웹 스크레이퍼 앱을 간단한 예로 사용했습니다. 코드의 비동기 부분은 네트워크 연결을 열고 사이트에서 읽는 루틴입니다. 인터리브하려는 모든 것입니다. 그러나이 모든 것을 시작하는 프로그램의 일부는 비동기 적이 지 않습니다. 비동기 작업을 시작한 다음 완료되면 정상적으로 종료됩니다.

잠재적으로 차단할 수있는 작업 을 비동기에서 분리하고 앱의 동기화 부분에 유지하는 것도 중요합니다  . 예를 들어 콘솔에서 사용자 입력을 읽으면 비동기 이벤트 루프를 포함한 모든 것이 차단됩니다. 따라서 비동기 작업을 시작하기 전이나 완료 한 후에 사용자 입력을 처리하려고합니다. (그것은 이다 비동기식 멀티 프로세싱 또는 스레딩을 통해 핸들 사용자 입력이 가능하지만 우리가 여기에받지 않습니다 진보 운동입니다.)

차단 작업의 몇 가지 예 :

  • 콘솔 입력 (방금 설명했듯이).
  • CPU 사용률이 높은 작업.
  • time.sleep강제로 일시 중지하는 데 사용 합니다. 을 asyncio.sleep대신 사용하여 비동기 함수 내에서 잠을 잘 수 있습니다 time.sleep.

2 단계 : 적절한 동기화 함수를 비동기 함수로 변환

프로그램의 어느 부분이 비동기 적으로 실행되는지 알고 나면, 함수로 분할하고 (아직하지 않은 경우) async키워드를 사용하여 비동기 함수로 전환 할 수 있습니다 . 그런 다음 애플리케이션의 동기 부분에 코드를 추가하여 비동기 코드를 실행하고 필요한 경우 결과를 수집해야합니다.

참고 : 비동기로 만든 각 함수의 호출 체인을 확인하고 잠재적으로 장기 실행 또는 차단 작업을 호출하지 않는지 확인해야합니다. 비동기 함수는 동기화 함수를 직접 호출 할 수 있으며 해당 동기화 함수가 차단되면이를 호출하는 비동기 함수도 마찬가지입니다.

동기화-비동기 변환이 작동하는 방법에 대한 간단한 예를 살펴 보겠습니다. "before"프로그램은 다음과 같습니다.

def a_function () : # 시간이 걸리는 비동기 호환 동작 def another_function () : # 일부 동기화 함수이지만 차단하지 않음 def do_stuff () : a_function () another_function () def main () : for _ in range (3) : do_stuff () main () 

의 세 인스턴스를 do_stuff비동기 작업으로 실행하려면 do_stuff (그리고 잠재적으로 접촉하는 모든) 비동기 코드로 전환해야 합니다. 전환의 첫 번째 단계는 다음과 같습니다.

import asyncio async def a_function () : # 시간이 걸리는 비동기 호환 동작 def another_function () : # 일부 동기화 함수이지만 차단하지 않는 비동기 def do_stuff () : await a_function () another_function () async def main ( ) : 작업 = [] for _ in range (3) : tasks.append (asyncio.create_task (do_stuff ())) await asyncio.gather (tasks) asyncio.run (main ()) 

에 대한 변경 사항을 확인합니다  main. 이제를 main 사용 asyncio하여의 각 인스턴스를 do_stuff동시 작업으로 시작한 다음 결과를 기다립니다 ( asyncio.gather). 또한의 a_function모든 인스턴스가 a_function나란히 실행되고 비동기 동작이 필요한 다른 함수와 함께 실행 되기를 원하기 때문에 비동기 함수로 변환 했습니다 .

한 단계 더 나아가고 싶다면 another_function비동기 로 변환 할 수도 있습니다 .

async def another_function () : # 일부 동기화 함수이지만 하나를 차단하지는 않습니다. async def do_stuff () : await a_function () await another_function () 

그러나 another_function 비동기로 만드는  것은 (우리가 언급했듯이) 우리 프로그램의 진행을 방해하는 어떤 것도하지 않기 때문에 과잉 일 것입니다. 또한 프로그램의 동기 부분이라고 부르면  another_function비동기로 변환해야하므로 프로그램이 필요 이상으로 복잡해질 수 있습니다.

3 단계 : Python 비동기 프로그램을 철저히 테스트

비동기 변환 프로그램은 프로덕션에 들어가기 전에 테스트를 거쳐 예상대로 작동하는지 확인해야합니다.

If your program is modest in size — say, a couple of dozen lines or so — and doesn’t need a full test suite, then it shouldn’t be difficult to verify that it works as intended. That said, if you’re converting the program to async as part of a larger project, where a test suite is a standard fixture, it makes sense to write unit tests for async and sync components alike.

Both of the major test frameworks in Python now feature some kind of async support. Python’s own unittest framework includes test case objects for async functions, and pytest offers pytest-asyncio for the same ends.

Finally, when writing tests for async components, you’ll need to handle their very asynchronousness as a condition of the tests. For instance, there is no guarantee that async jobs will complete in the order they were submitted. The first one might come in last, and some might never complete at all. Any tests you design for an async function must take these possibilities into account.

How to do more with Python

  • Get started with async in Python
  • How to use asyncio in Python
  • How to use PyInstaller to create Python executables
  • Cython tutorial: How to speed up Python
  • How to install Python the smart way
  • How to manage Python projects with Poetry
  • How to manage Python projects with Pipenv
  • Virtualenv and venv: Python virtual environments explained
  • Python virtualenv and venv do’s and don’ts
  • Python threading and subprocesses explained
  • How to use the Python debugger
  • timeit을 사용하여 Python 코드를 프로파일 링하는 방법
  • cProfile을 사용하여 Python 코드를 프로파일 링하는 방법
  • Python을 JavaScript로 변환하는 방법 (그리고 다시)