Python에서 asyncio를 사용하여 비동기식 동작을 수행합니다.

이 글은 처음으로 my blog.에 발표되었다
파이톤의 중장기 운행을 미나리로 처리하는 비동기 임무에 대한 게시물이 지난번에 발표된 이후 많은 변화가 생겼다.지난번에 우리는 그것으로 몇 개의 비동기적인 작업을 실행하여 some service which took around 1-5 minutes에서 데이터를 얻었다.
비록 나는 여전히 미나리를 사용하여 이런 유형의 장시간 운행하는 임무를 처리할 것이지만, 일부 임무는 실행 라인 내부에서 더욱 잘 처리된다.오늘 우리는 이런 조작을 처리하는 방법을 토론할 것이다.
나를 아는 사람에게 노드를 쓸 수 있도록 새로운 캐릭터를 바꿨다.1년여 전에 나는 줄곧 이 일을 하고 있었다.내가 그것을 사용하여 API를 작업하고 구축하는 과정에서, 나는 Node가 Async behavior and long-running tasks을 처리하는 방식에 미련을 가지기 시작했다.
생각은 간단합니다. 만약 작업이 IO를 기다리고 있다면, 우리는 다른 작업을 동시에 실행할 수 있습니다.그것이 필요한 부분만이 그것을 기다릴 수 있다.
파이톤에서 같은 개념을 사용하고 싶어요.다행히도 파이썬 버전 3.4에서 같은 목적을 위해 asyncio을 도입했다.이 글에서 우리는 그것을 토론하고 그것의 중요성을 이해하려고 한다.
본고가 끝날 때, 당신은 비동기 함수의 중요성을 이해할 수 있고, 코드 라이브러리에서 그것들을 사용하기 시작할 수 있습니다.

선결 조건


이 강좌에서 사용한 것은 Python 3.8입니다. 이 예시들을 실행하고 시도하기를 원할 수도 있습니다.너는 online as well을 시험해 볼 수 있다.

Python의 기본 비동기 함수


이것이 바로 우리가 파이톤으로 기본 비동기 함수를 작성하는 방법이다.
import asyncio

async def func1(a):
    print(f"started func 1: {a + 1}")
    await asyncio.sleep(1)
    return a + 1

asyncio.run(func1(1))
응수
started func 1: 2
2
먼저 asyncio 모듈을 가져와야 합니다.
비동기 함수의 정의는 일반 함수와 마찬가지로 키워드 async만 추가하면 된다.만약 당신이 coroutine의 실행을 기다리고 싶다면, 키워드 await을 사용해야 합니다.
마지막으로 이 함수를 asyncio.run 방법으로 실행할 수 있다.

Anytime you use the await keyword inside the async function, the thread will not move forward until we get a response from the called function.


이 간단한 실현은 당신이 비동기 함수를 사용하는 장점을 이해하는 데 도움이 되지 않을 것이다.다음 절에서, 우리는 집행에 필요한 시간을 토론할 것이며, 당신은 진정으로 차이를 보기 시작할 것이다.

전체 실행에 소요되는 시간

async 함수를 사용하는 장점을 알아보기 위해 우리는 asyncsync 행위에서 같은 코드를 실행하는 데 필요한 시간을 비교할 것이다.
우선 비동기 코드를 작성합시다.
import asyncio
import time
start = time.time()

async def async_sleep():
    await asyncio.sleep(1)

async def func1(a):
    print(f"started func 1: {a + 1}")
    await async_sleep()
    return a + 1

async def func2(a):
    print(f"started func 2: {a + 2}")
    await async_sleep()
    return a + 2

async def func3(a):
    print(f"started func 3: {a + 3}")
    await async_sleep()
    return a + 3

async def func4(a):
    print(f"started func 4: {a + 4}")
    await async_sleep()
    return a + 4

async def main():
    tasks = (func1(1), func2(1), func3(1), func4(1))
    await asyncio.gather(*tasks)
    print(f"Completed after: {time.time() - start}")

asyncio.run(main())
나는 네 개의 다른 함수를 정의했는데, 그것들의 작용은 거의 같다.async_sleep이라는 장기 운행 함수가 IO 작업을 수행하고 있다고 가정하십시오.예를 들어 데이터베이스에서 데이터를 얻다.

asyncio.gather() is used to run the tasks passed to it concurrently.


이 코드를 실행한 후의 응답은 다음과 같습니다.
started func 1: 2
started func 2: 3
started func 3: 4
started func 4: 5
Completed after: 1.1852593421936035
함수는 실행할 때마다 1~2초 사이에 실행됩니다.(검사를 위해 다섯 번 실행했다).
이제 같은 코드의 sync 버전을 실행해 봅시다.
import time
start = time.time()

def sync_sleep():
    time.sleep(1)

def func1(a):
    print(f"started func 1: {a + 1}")
    sync_sleep()
    return a + 1

def func2(a):
    print(f"started func 2: {a + 2}")
    sync_sleep()
    return a + 2

def func3(a):
    print(f"started func 3: {a + 3}")
    sync_sleep()
    return a + 3

def func4(a):
    print(f"started func 4: {a + 4}")
    sync_sleep()
    return a + 4

def main():
    func1(1)
    func2(1)
    func3(1)
    func4(1)
    print(f"Completed after: {time.time() - start}")

main()
이 작업을 수행한 후 다음과 같은 응답을 받았습니다.
started func 1: 2
started func 2: 3
started func 3: 4
started func 4: 5
Completed after: 4.168870687484741
이는 4개의 함수를 실행하기 위해 ~3초를 절약했다.현재 만약에 우리가 10000개의 이런 함수를 운행하고 싶다면 우리는 ~2000초, 즉 35-40분을 절약할 수 있다.
너무 좋아요, 그렇죠?
설령 우리가 매번 교체할 때마다 2-3개의 이런 함수를 운행한다고 해도 이러한 작은 절약은 고객에게 더 좋은 사용자 체험을 제공하는 데 매우 중요하다.
현재 우리는 async 함수를 사용하는 장점을 이해했기 때문에 그것에 대해 더 많은 것을 알아야 한다.

비동기 함수의 실행 순서


우리는 비동기 모드에서 함수를 운행할 때 함수의 집행 순서를 진정으로 이해하지 못한다는 것을 이해해야 한다.우리는 하나의 예로 이 개념을 이해합시다.
이 함수들은 실행이 끝난 후 바로 되돌아옵니다.
우리는 random.randint(0, 10) * 0.1을 사용하여 0과 1 사이의 무작위 수면을 통해 서로 다른 실행 시간을 시뮬레이션할 수 있다.
이것은 완전한 코드다.
import asyncio
import time
import random
start = time.time()

def random_sleep_time():
    return random.randint(0, 10) * 0.1

async def async_sleep():
    await asyncio.sleep(random_sleep_time())

async def func1(a):
    await async_sleep()
    print(f"completed func {a}: {a + 1}")
    return a + 1

async def main():
    tasks = [func1(a) for a in range(0, 5)]
    await asyncio.gather(*tasks)
    print(f"Completed after: {time.time() - start}")

asyncio.run(main())
다음 코드에 대한 응답은,
completed func 0: 1
completed func 3: 4
completed func 1: 2
completed func 2: 3
completed func 4: 5
Completed after: 0.9764895439147949

실제 사용


Python 3.8 발표 후 asyncio.run() 방법을 안정적인 API로 이동하면 문제 없이 사용할 수 있습니다.synchronous 방법에서 비동기적인 방법으로 전환하는 유일한 문제는 사고 방식을 바꾸는 것이다.너는 모든 세부 사항을 생각하는 방식을 바꿔야 한다.일단 네가 비동기 함수를 생각하면, 나를 믿어라. 너는 그것을 좋아할 것이다.

그것은 엔진 뚜껑 아래에서 어떻게 작동합니까?


비동기 함수의 유형을 검사합시다.
type(main())

# <class 'coroutine'>
따라서 비동기 함수의 유형은 coroutine이다.
GeeksForGeeks article은 협동 절차를 잘 설명한다.
그들이 댓글에 쓴 내용을 총괄해 보면 서브루틴은 하나의 입구점이 있고 연속적으로 실행되는 명령이다.
다른 한편, 협동 프로그램은 다른 협동 프로그램에 통제권을 일시 정지하거나 넘겨주어 여러 임무를 동시에 실행할 수 있다.
Python은 비동기적인 행동을 허용하기 위해 이 개념을 사용합니다.

비동기 함수에서의 시간 초과 처리


작업 완료를 기다리는 시간 초과가 있어야 합니다.우리는 그것의 완성을 영원히 기다려서는 안 된다.asyncio은 비동기 함수에 시간 초과를 추가하여 비동기 함수가 완성되기 전에 실행을 건너뛸 수 있도록 합니다.
응용 프로그램에서 타사 API를 호출하고 타사 자체가 종료된 경우 이러한 실제 응용 프로그램이 나타날 수 있습니다.이런 상황에서 너는 오랫동안 기다리고 싶지 않다.timeout 방법을 사용하여 문제를 해결할 수 있습니다.코드 예 참조:
async def async_sleep():
    await asyncio.sleep(2)
    print('Execution completed')

async def main():
    try:
        await asyncio.wait_for(async_sleep(), timeout=1.0)
    except asyncio.TimeoutError:
        print('Timeout error')

asyncio.run(main())
주어진 TimeoutError을 전달하기 전에 협동 루트가 되돌아오지 않으면 비동기 함수는 timeout을 일으킨다.
상기 코드를 실행한 후의 응답은
Timeout error
asyncio 모듈에는 또 다른 좋은 방법이 있습니다.
당신은 python's official website으로 전화를 걸어 조회할 수 있습니다.

결론


사용자에게 더 좋고 더 빠른 체험을 제공하려면 응용 프로그램에서 asyncio 모듈을 사용하십시오.이는 API 실행 시간 또는 기타 모든 작업을 줄일 수 있는 방법을 찾는 데 도움이 될 것입니다.
단지 몇 달 동안 이렇게 쓰도록 강요하는 것만으로도 미래에 커다란 보답을 얻을 수 있다.당신의 생각을 듣고 싶으면 아래의 평론을 사용하세요.

좋은 웹페이지 즐겨찾기