programing

Python에서 가능한 가장 간단한 비동기/대기 예제

yellowcard 2023. 6. 4. 10:27
반응형

Python에서 가능한 가장 간단한 비동기/대기 예제

게시물,에 대한 많은asyncio/async/awaitPython 3.5+에서는 많은 것이 복잡했으며, 제가 찾은 가장 간단한 것은 아마도 이것일 것입니다.
여전히 사용합니다.ensure_future그리고 파이썬의 비동기 프로그래밍에 대한 학습 목적을 위해, 저는 훨씬 더 최소한의 예와 기본적인 비동기/대기 예제를 수행하는 데 필요한 최소한의 도구가 무엇인지 알고 싶습니다.

질문: 이 두 키워드 + 코드만 사용하여 비동기 루프 + 다른 파이썬 코드를 실행하고 다른 것은 실행하지 않는 방식을 보여주는 간단한 예를 제공할 수 있습니까?asyncio함수?

예: 다음과 같은 것이 있습니다.

import asyncio

async def async_foo():
    print("async_foo started")
    await asyncio.sleep(5)
    print("async_foo done")

async def main():
    asyncio.ensure_future(async_foo())  # fire and forget async_foo()
    print('Do some actions 1')
    await asyncio.sleep(5)
    print('Do some actions 2')

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

그나없이 없이.ensure_future하는 방식을 .wait/wait/sync 파일을 참조하십시오.

당신의 질문에 답하기 위해, 저는 동일한 문제에 대해 세 가지 다른 해결책을 제공할 것입니다.

사례 1: 일반 파이썬

import time

def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

def sum(name, numbers):
    total = 0
    for number in numbers:
        print(f'Task {name}: Computing {total}+{number}')
        sleep()
        total += number
    print(f'Task {name}: Sum = {total}\n')

start = time.time()
tasks = [
    sum("A", [1, 2]),
    sum("B", [1, 2, 3]),
]
end = time.time()
print(f'Time: {end-start:.2f} sec')

출력:

Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6

Time: 5.02 sec

사례 2: 비동기/대기가 잘못됨

import asyncio
import time

async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

async def sum(name, numbers):
    total = 0
    for number in numbers:
        print(f'Task {name}: Computing {total}+{number}')
        await sleep()
        total += number
    print(f'Task {name}: Sum = {total}\n')

start = time.time()

loop = asyncio.get_event_loop()
tasks = [
    loop.create_task(sum("A", [1, 2])),
    loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

end = time.time()
print(f'Time: {end-start:.2f} sec')

출력:

Task A: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 0+1
Time: 2.01
Task B: Computing 1+2
Time: 3.01
Task B: Computing 3+3
Time: 4.01
Task B: Sum = 6

Time: 5.01 sec

사례 3: 비동기/대기가 올바르게 완료되었습니다.

사례 2와 동일하지만, 예외는sleep함수:

async def sleep():
    print(f'Time: {time.time() - start:.2f}')
    await asyncio.sleep(1)

출력:

Task A: Computing 0+1
Time: 0.00
Task B: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task B: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 3+3
Time: 2.00
Task B: Sum = 6

Time: 3.01 sec

사례 1과 사례 2는 동일한 5초를 제공하는 반면, 사례 3은 3초만 제공합니다.따라서 제대로 수행된 비동기/대기 시간이 더 빠릅니다.

차이의 이유는 다음의 구현 내에 있습니다.sleep기능.

# Case 1
def sleep():
    ...
    time.sleep(1)

# Case 2
async def sleep():
    ...
    time.sleep(1)

# Case 3
async def sleep():
    ...
    await asyncio.sleep(1)

사례 1과 사례 2는 "동일"합니다. 즉, 다른 사용자가 리소스를 사용하도록 허용하지 않고 "휴면"합니다.반면 사례 3에서는 리소스가 절전 모드에 있을 때 액세스할 수 있습니다.

사례 2에서, 우리는 추가했습니다.async정상적인 기능합니다.그러나 이벤트 루프는 중단 없이 실행됩니다. 이유는 무엇입니까?왜냐하면 우리는 루프가 다른 작업을 실행하기 위해 당신의 기능을 방해할 수 있는 위치를 말하지 않았기 때문입니다.

사례 3에서, 우리는 다른 작업을 실행하기 위해 기능을 중단해야 하는 정확한 위치를 이벤트 루프에 알려주었습니다.정확히 어디?바로 여기에요!

await asyncio.sleep(1)

자세한 내용은 여기를 참조하십시오.

읽는 것을 고려합니다.

방법을 보여주는 간단한 예를 제시하는 것이 가능합니까?async/await하면 작동합니다 + 이 두 키 워 드 만 작 니 다 합 동 면 하 용 사 다 ▁+ 니asyncio.get_event_loop()+run_until_complete 코드는 파이썬 코드는 .asyncio함수?

이렇게 하면 다음과 같은 코드를 작성할 수 있습니다.

import asyncio


async def main():
    print('done!')


if __name__ ==  '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

하지만 이런 식으로 비동기식이 필요한 이유를 설명하는 것은 불가능합니다.

당신은 왜 데런당, 이겁까니필요한이 필요합니까?asyncio단순한 코드가 아니라구요?답은 다음과 같습니다.asyncio네트워크에 대한 읽기/쓰기와 같은 I/O 차단 작업을 병렬화할 때 성능 이점을 얻을 수 있습니다.그리고 유용한 예를 작성하려면 이러한 작업의 비동기 구현을 사용해야 합니다.

자세한 설명은 이 답변을 참조하시기 바랍니다.

업데이트:

예들어다사같다니용합을 .asyncio.sleep 및 I/O 차단 작업을 합니다.asyncio.gather여러 차단 작업을 동시에 실행할 수 있는 방법을 보여 줍니다.

import asyncio


async def io_related(name):
    print(f'{name} started')
    await asyncio.sleep(1)
    print(f'{name} finished')


async def main():
    await asyncio.gather(
        io_related('first'),
        io_related('second'),
    )  # 1s + 1s = over 1s


if __name__ ==  '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

출력:

first started
second started
first finished
second finished
[Finished in 1.2s]

다 어떻게 참고하세요.io_related시작한 지 불과 1초 만에 둘 다 끝났습니다.

Python 3.7+는 이제 더 간단한 문구("ure_future"보다 기억하기 쉽다)와 함께 더 간단한 API를 가지고 있습니다.create_task작업 개체를 반환합니다(필요한 경우 나중에 작업을 취소하는 데 유용할 수 있음).

기본 예제 1

import asyncio

async def hello(i):
    print(f"hello {i} started")
    await asyncio.sleep(4)
    print(f"hello {i} done")

async def main():
    task1 = asyncio.create_task(hello(1))  # returns immediately, the task is created
    await asyncio.sleep(3)
    task2 = asyncio.create_task(hello(2))
    await task1
    await task2

asyncio.run(main())  # main loop

결과:

안녕하세요 1 시작했습니다
안녕하세요 2 시작했습니다
안녕하세요 1개 완료
안녕하세요 2개 완료


기본 사례 2

이러한 비동기 함수의 반환 값을 얻어야 한다면,gather유용합니다.다음 예는 설명서에서 영감을 얻은 것입니다.

import asyncio

async def factorial(n):
    f = 1
    for i in range(2, n + 1):
        print(f"Computing factorial({n}), currently i={i}...")
        await asyncio.sleep(1)
        f *= i
    return f

async def main():
    L = await asyncio.gather(factorial(2), factorial(3), factorial(4))
    print(L)  # [2, 6, 24]

asyncio.run(main())

예상 출력:

요인(2)을 계산하는 중, 현재 i=2...
요인 계산 중(3), 현재 i=2...
요인 계산 중(4), 현재 i=2...
요인 계산 중(3), 현재 i=3...
요인 계산 중(4), 현재 i=3...
요인 계산 중(4), 현재 i=4...
[2, 6, 24]


에도: 더도라사하용.asyncio그리고 아닌trio후자의 튜토리얼은 제가 파이썬 비동기 프로그래밍을 하는 데 도움이 되었습니다.

모든 것이 잘 설명되어 있으므로 동기식 코드와 비동기식 코드를 비교하는 이벤트 루프의 예를 몇 가지 실행해 보겠습니다.

동기 코드:

import time

def count():
    time.sleep(1)
    print('1')
    time.sleep(1)
    print('2')
    time.sleep(1)
    print('3')

def main():
    for i in range(3):
        count()

if __name__ == "__main__":
    t = time.perf_counter()
    main()
    t2 = time.perf_counter()
    
    print(f'Total time elapsed: {t2:0.2f} seconds')

출력:

1
2
3
1
2
3
1
2
3
Total time elapsed: 9.00 seconds

다음 사이클이 시작되기 전에 카운트가 완료될 때까지 실행되는 각 사이클을 볼 수 있습니다.

비동기 코드:

import asyncio
import time

async def count():
    await asyncio.sleep(1)
    print('1')
    await asyncio.sleep(1)
    print('2')
    await asyncio.sleep(1)
    print('3')

async def main():
    await asyncio.gather(count(), count(), count())

if __name__ == "__main__":
    t = time.perf_counter()
    asyncio.run(main())
    t2 = time.perf_counter()

    print(f'Total time elapsed: {t2:0.2f} seconds')

출력:

1
1
1
2
2
2
3
3
3
Total time elapsed: 3.00 seconds

반면에 비동기식 등가물은 9초가 아니라 3초가 걸린 것처럼 보입니다.첫 번째 카운트 사이클이 시작되었고 그것이 도달하자마자awaitsleep one Python은 다른 작업을 자유롭게 수행할 수 있었습니다. 예를 들어, second를 시작한 후 세 번째 카운트 사이클을 시작하는 것입니다.이것이 우리가 모든 튜브보다 모든 튜브를 가지고 있는 이유입니다. 세 개 모두.출력에서 프로그래밍은 동시에 매우 유용한 도구가 될 수 있습니다.멀티프로세싱은 모든 멀티태스킹 작업을 수행하며, 파이썬에서는 다중 코어 CPU에서 프로그램을 실행하는 멀티코어 동시성을 위한 유일한 옵션입니다.스레드를 사용하는 경우 운영 체제는 여전히 모든 멀티태스킹 작업을 수행하고 있으며 글로벌 인트레퍼터 잠금에서는 비동기 프로그래밍에서 멀티코어 동시성을 방지합니다.운영 체제 개입은 없습니다. 한 프로세스가 스레드 하나이므로 작업이 잘 진행되고 있는 것은 대기 기간이 있을 때 CPU를 해제하여 다른 작업이 사용할 수 있도록 할 수 있도록 할 수 있습니다.

import asyncio

loop = asyncio.get_event_loop()


async def greeter(name):
    print(f"Hi, {name} you're in a coroutine.")

try:
    print('starting coroutine')
    coro = greeter('LP')
    print('entering event loop')
    loop.run_until_complete(coro)
finally:
    print('closing event loop')
    loop.close()

출력:

starting coroutine
entering event loop
Hi, LP you're in a coroutine.
closing event loop

비동기 프레임워크에는 일반적으로 이벤트 루프라고 하는 스케줄러가 필요합니다.이 이벤트 루프는 실행 중인 모든 작업을 추적하고 기능이 일시 중단되면 이벤트 루프로 제어를 반환하여 시작하거나 다시 시작할 다른 기능을 찾으며 이를 협동 멀티태스킹이라고 합니다.비동기 IO는 이 이벤트 루프를 중심으로 하는 비동기 프레임워크를 제공하며, 애플리케이션이 이벤트 루프와 상호 작용하는 입출력 이벤트를 효율적으로 처리하여 실행할 코드를 명시적으로 등록한 다음 리소스를 사용할 수 있을 때 이벤트 스케줄러가 필요한 호출을 애플리케이션 코드로 루프할 수 있도록 합니다.따라서 네트워크 서버가 소켓을 열고 입력 이벤트가 발생할 때 소켓을 등록하면 이벤트 루프는 새로운 수신 연결이 있거나 읽을 데이터가 있을 때 서버 코드에 알림을 보냅니다.소켓에서 읽을 데이터가 서버보다 더 없을 경우 이벤트 루프에 대한 제어 권한이 반환됩니다.

제어를 이벤트 루프로 되돌리는 메커니즘은 동시 작업을 위해 설계된 언어 구조인 공동 루틴에 따라 달라집니다.co-routine은 다른 co-routine과 함께 awake 키워드를 사용하여 실행을 일시 중지할 수 있으며, 일시 중지된 동안 co-routine 상태가 유지되어 co-routine 상태가 유지되어 co-routine 중 하나가 중단된 곳에서 다시 시작된 다음 결과를 기다릴 수 있습니다. 그러면 작업을 재사용 가능한 부분으로 쉽게 분해할 수 있습니다.

import asyncio

loop = asyncio.get_event_loop()

async def outer():
    print('in outer')
    print('waiting for result 1')
    result1 = await phase1()
    print('waiting for result 2')
    result2 = await phase2(result1)
    return result1, result2


async def phase1():
    print('in phase1')
    return 'phase1 result'

async def phase2(arg):
    print('in phase2')
    return 'result2 derived from {}'.format(arg)

asyncio.run(outer())

출력:

in outer
waiting for result 1
in phase1
waiting for result 2
in phase2

이 예에서는 순서대로 실행해야 하지만 다른 작업과 동시에 실행할 수 있는 두 단계를 묻습니다.awake제어 흐름이 이미 루프에 의해 관리되는 공유 흐름 내부에 있기 때문에 새로운 공유 흐름을 루프에 추가하는 대신 키워드를 사용합니다.새로운 공동 루틴을 관리하기 위해 루프에 알릴 필요는 없습니다.

왜 그런지는 모르겠지만 이 주제에 대한 모든 설명이 너무 복잡하거나 쓸모없는 비동기식 예제를 사용하고 있습니다.잠을 자요...지금까지 찾은 최고의 코드 샘플은 다음과 같습니다. https://codeflex.co/python3-async-await-example/ 입니다.

단순..좋아요.. 정말 멋져요..

  import asyncio
  import time
  import random

  async def eat():
     wait = random.randint(0,3)
     await asyncio.sleep(wait)
     print("Done With Eating")

  async def sleep():
     wait = random.randint(0,3)
     await asyncio.sleep(wait)
     print("Done With Sleeping")

  async def repeat():
     wait = random.randint(0,3)
     await asyncio.sleep(wait)
     print("Done With Repeating")

  async def main():
     for x in range(5):
        await asyncio.gather(eat(),sleep(),repeat())
        time.sleep(2)
        print("+","-"*20)

  if __name__ == "__main__":
     t = time.perf_counter()
     asyncio.run(main())
     t2 = time.perf_counter()

     print(f'Total time elapsed: {t2:0.2f} seconds')

모두가 전환에 집중하는 것 같습니다.time.sleepasyncio.sleep하지만 현실 세계에서는, 그것이 항상 가능하지 않습니다.때로는 API 호출을 할 수 있는 라이브러리 호출을 해야 합니다(예: 구글에서 서명된 URL 요청).

사용할 수 있는 방법은 다음과 같습니다.time.sleep하지만 비동기식으로:

import asyncio
import time
from concurrent.futures.thread import ThreadPoolExecutor

def sleep():
    print(f'Time: {time.time() - start:.2f}')
    time.sleep(1)

async def sum(name, numbers):
    _executor = ThreadPoolExecutor(2)
    total = 0
    for number in numbers:
        print(f'Task {name}: Computing {total}+{number}')
        await loop.run_in_executor(_executor, sleep)
        total += number
    print(f'Task {name}: Sum = {total}\n')

start = time.time()

loop = asyncio.get_event_loop()
tasks = [
    loop.create_task(sum("A", [1, 2])),
    loop.create_task(sum("B", [1, 2, 3])),
]
loop.run_until_complete(asyncio.wait(tasks))
loop.close()

end = time.time()
print(f'Time: {end-start:.2f} sec')

출력:

Task A: Computing 0+1
Time: 0.00
Task B: Computing 0+1
Time: 0.00
Task A: Computing 1+2
Time: 1.00
Task B: Computing 1+2
Time: 1.00
Task A: Sum = 3

Task B: Computing 3+3
Time: 2.01
Task B: Sum = 6

Time: 3.01 sec
import asyncio
import requests

async def fetch_users():
    response = requests.get('https://www.testjsonapi.com/users/')
    users = response.json()
    return users

async def print_users():
    # create an asynchronous task to run concurrently 
    # which wont block executing print statement before it finishes
    response = asyncio.create_task(fetch_users())
    print("Fetching users ")
    # wait to get users data from response before printing users
    users = await response

    for user in users:
        print(f"name : {user['name']} email : {user['email']}")

asyncio.run(print_users())
print("All users printed in console")

출력은 다음과 같이 표시됩니다.

Fetching users
name : Harjas Malhotra email : harjas@gmail.com
name : Alisha Paul email : alisha@gmail.com
name : Mart Right email : marrk9658@yahoo.com
name : Brad Pitter email : brad@gmail.com
name : Ervin Dugg email : Ervin69@gmail.com 
name : Graham Bell email : Graham@bell.biz
name : James Rush email : james369@hotmail.com
name : Deepak Dev email : deepak@gmail.com
name : Ajay Rich email : therichposts@gmail.com
All users printed in console

코드가 어떻게 작동하는지 보겠습니다.이 선우파이전화때할이썬때▁python▁call할전화를 부를 때.print_users()완료될 때까지 아래의 인쇄 문이 실행되지 않도록 합니다.안으로 에.print_users()이 해당 작업과 될 수 됩니다. 이 은 시동작업 이 동 실 될 수 있 행 습 니 다 에 시 작 과 업 해 당 생 이 문 성 되 의 아 어 래 ▁will ▁which ▁a 니 ▁task 다 ▁simultane ▁so ▁be ments ▁taskfetch_users()에 이 때 ㅠㅠ. 해당 시간에 이 작업이 실행될 때Fetching users콘솔에서 인쇄됩니다. 후 다▁wait▁after▁will▁for▁response니의 응답을 기다릴 것입니다.fetch_users() 전에안 때문입니다. 에. 완료 후.fetch_users()모든 사용자 이름과 이메일이 콘솔에 인쇄됩니다. 따서라가 된 후에, 후료완이 됩니다.print_users()아래에 있는 print 문이 실행됩니다.

비록 위에 있는 몇몇 대답들이 약간 추상적이었지만.

from datetime import datetime
import asyncio




async def time_taking(max_val,task_no):
    print("**TASK STARTING TO EXECUTE CONCURRENT TASk NO {} ***".format(task_no))

    await asyncio.sleep(2)
    value_list = []
    for i in range(0,max_val):
        value_list.append(i)

    print("****FINSIHING UP TASk NO {}  **".format(task_no))
    return value_list



async def test2(task_no):
    await asyncio.sleep(5)
    print("**TASK STARTING TO EXECUTE CONCURRENT TASk NO {} ***".format(task_no))
    await asyncio.sleep(5)
    print("****FINSIHING UP  TASk NO {}  **".format(task_no))

async def function(value = None):
    tasks = []
    start_time = datetime.now()
    
    # CONCURRENT TASKS
    tasks.append(asyncio.create_task(time_taking(20,1)))
    tasks.append(asyncio.create_task(time_taking(43,2)))
    tasks.append(asyncio.create_task(test2(3)))
    
    # concurrent execution
    lists = await asyncio.gather(*tasks)
    end_time = datetime.now()
    
    time_taken = end_time - start_time
    return lists,time_taken


# run inside event loop 
res,time_taken = asyncio.run(function())

print(res,time_taken)

다음은 매우 간단하고 원활한 예입니다.

import asyncio

async def my_task1():
    print("Task 1 started")
    await asyncio.sleep(1)  # some light io task
    print("Task 1 completed")
    return "Done1"

async def my_task2():
    print("Task 2 started")
    await asyncio.sleep(2)  # some heavy io task
    print("Task 2 completed")
    return "Done2"

async def main():
    # both the functions are independent of each other, 
    # as tasks gets completes, `.gather` keeps on storing the results
    results = await asyncio.gather(my_task1(), my_task2())
    print(f"The results are {results}")
    
    # if task1 is dependent on completion of task2, then use this
    ret1 = await my_task2()
    ret2 = await my_task1()
    print(f"The ret1: {ret1} ret2 {ret2}")

asyncio.run(main())

언급URL : https://stackoverflow.com/questions/50757497/simplest-async-await-example-possible-in-python

반응형