2017-12-18 33 views
2

asyncio를 처음 사용한다고 가정 해 보겠습니다. 현재 프로젝트를 병렬 처리하기 위해 async/await를 사용하고 있으며 내 coroutines를 모두 asyncio.ensure_future으로 전달하고 있음을 알았습니다. 이런 것들의 많은 :Python asyncio ensure_future decorator

유휴 코 루틴 대신 실행 작업을 반환하는 비동기 함수에 대한 호출을 위해 무엇 내가 좋아하는 정말 것
coroutine = my_async_fn(*args, **kwargs) 
task = asyncio.ensure_future(coroutine) 

. 나는 내가하려고하는 것을 이루기 위해 장식자를 창조했다.

def make_task(fn): 
    def wrapper(*args, **kwargs): 
     return asyncio.ensure_future(fn(*args, **kwargs)) 
    return wrapper 

@make_task 
async def my_async_func(*args, **kwargs): 
    # usually making a request of some sort 
    pass 

asyncio에는이 작업을 수행하는 기본 방법이 있습니까? 이 문제가 시작되면 asyncio를 잘못 사용하고 있습니까?

답변

2

asyncio에는이 작업을 수행하는 기본 방법이 있습니까? 을 (를) 찾을 수 없습니까?

아니요, 아니요, asyncio에는 coroutine-functions를 작업으로 전송할 데코레이터가 없습니다.

제가이 문제로 시작한다면 잘못 했나요?

당신이하고있는 것을 보지 않고 말하기는 어렵지만, 사실일지도 모릅니다. asyncio 프로그램에서 작업을 만드는 것은 일반적인 작업이지만, 나는 당신이이 작업을 항상해야하는 많은 coroutine을 만들지는 모르겠다. "비동기 적으로 어떤 함수를 호출"하는 방법입니다,하지만이 완료 될 때까지 현재의 실행 흐름을 차단 -

코 루틴을 위해 기다리는 :

await some() 

# you'll reach this line *only* when some() done 

작업을 다른 한편으로는 - 방법을 "실행 기능 in background"입니다 우리가 asyncio 프로그램을 작성할 때 우리는 일반적으로 다음 일을 시작하기 전에 몇 가지 작업의 결과를 필요로하기 때문에

task = asyncio.ensure_future(some()) 

# you'll reach this line immediately 

은 우리가 일반적으로 첫 번째 방법이 필요합니다 :

, 그것은 현재의 실행 흐름을 차단하지 않습니다
text = await request(url) 

links = parse_links(text) # we need to reach this line only when we got 'text' 

다른 한편으로 작업을 만드는 것은 대개 다음 코드가 작업 결과에 의존하지 않는다는 것을 의미합니다. 그러나 다시는 항상 그런 일은 일어나지 않습니다. asyncio.gather이 사용하는 것입니다 달성하기

# wrong way to run concurrently: 
asyncio.ensure_future(request(url1)) 
asyncio.ensure_future(request(url2)) 
asyncio.ensure_future(request(url3)) 

올바른 방법 :

# correct way to run concurrently: 
await asyncio.gather(
    request(url1), 
    request(url2), 
    request(url3), 
) 

이 될 수

ensure_future 이후 수익률은 즉시 어떤 사람들은 concurently 일부 코 루틴을 실행하는 방법으로 그것을 사용하려고 니가 원하는거야?

UPD :

나는 귀하의 경우 사용하여 작업이 좋은 아이디어라고 생각합니다. 하지만 당신은 데코레이터를 사용해야한다고 생각하지 않습니다. (요청을하기 위해) 코 루틴 (coroutine) 기능은 여전히 ​​구체적인 사용법의 세부 사항 (작업으로 사용됨)과 별개의 부분입니다.요청 동기화 제어가 주요 기능과 다른 경우 동기화를 별도의 기능으로 이동하는 것이 좋습니다. 나는 이런 식으로 뭔가를 할 것이다 :

import asyncio 


async def request(i): 
    print(f'{i} started') 
    await asyncio.sleep(i) 
    print(f'{i} finished') 
    return i 


async def when_ready(conditions, coro_to_start): 
    await asyncio.gather(*conditions, return_exceptions=True) 
    return await coro_to_start 


async def main(): 
    t = asyncio.ensure_future 

    t1 = t(request(1)) 
    t2 = t(request(2)) 
    t3 = t(request(3)) 
    t4 = t(when_ready([t1, t2], request(4))) 
    t5 = t(when_ready([t2, t3], request(5))) 

    await asyncio.gather(t1, t2, t3, t4, t5) 


if __name__ == '__main__': 
    loop = asyncio.get_event_loop() 
    try: 
     loop.run_until_complete(main()) 
    finally: 
     loop.run_until_complete(loop.shutdown_asyncgens()) 
     loop.close() 
+0

감사합니다. 나는 asyncio.gather를 알고 있었지만 coroutine의 가장 큰 문제점 중 하나는 단지 한 번만 기다릴 수 있다는 것입니다. 내가하려는 것은 다음과 같다. 3 개의 요청을 보낸다. 요청 1과 2가 끝나면 요청 4를 전송합니다. 요청 2와 3이 끝나면 요청 5를 보내라. 나는 4 번 작업에 1, 2 번 작업을 보내고 5 번 작업에서 2, 3 번 작업을 모으고있다. 요청 5가 전송되기 전에 요청 1을 완료하거나 요청 4가 전송하기 전에 요청 3을 완료 할 때까지 기다릴 필요가 없습니다. –

+0

@BrettBeatty 나는 대답을 업데이트했다. –

+0

흠 흥미로운 접근 방법입니다. 마지막으로 t4와 t5 만 수집하면됩니다. –

2

asyncio은 초기 시험판 버전 @task 장식을했지만, 우리는 그것을 제거.

이유는 데코레이터가 사용할 루프를 알지 못하기 때문입니다. asyncio는 가져 오기에서 루프를 인스턴스화하지 않으며 테스트 스위트는 일반적으로 테스트 분리를 위해 테스트 당 새 루프를 만듭니다.

+0

알아두면 재미 있습니다. 감사. –