Python의 asyncio 모듈을 사용하여 동시 작업을 올바르게 생성하고 실행하는 방법은 무엇입니까?
TaskPython 3의 비교적 새로운 asyncio모듈을 사용하여 동시에 실행되는 두 개의 객체 를 올바르게 이해하고 구현하려고 합니다.
간단히 말해서, asyncio Task는 이벤트 루프를 통한 비동기 프로세스 및 동시 실행 을 처리하도록 설계된 것 같습니다 . await이벤트 루프를 차단하지 않고 결과를 기다렸다가 사용할 수있는 콜백없는 방법으로 (비동기 함수에 적용됨) 사용을 촉진합니다 . (미래와 콜백은 여전히 실행 가능한 대안입니다.)
또한 코 루틴을 래핑하도록 설계된 asyncio.Task()의 특수 하위 클래스 인 클래스를 제공합니다 Future. asyncio.ensure_future()메서드 를 사용하여 호출하는 것이 좋습니다. asyncio 작업의 용도는 독립적으로 실행되는 작업이 동일한 이벤트 루프 내에서 다른 작업과 '동시에'실행되도록하는 것입니다. 내 이해는 Tasks이벤트 루프에 연결되어 await명령문 사이의 코 루틴을 자동으로 계속 구동한다는 것 입니다.
Executor클래스 중 하나를 사용할 필요없이 동시 태스크를 사용할 수 있다는 생각이 마음에 들지만 구현에 대한 자세한 내용을 찾지 못했습니다.
이것이 내가 현재하는 방법입니다.
import asyncio
print('running async test')
async def say_boo():
i = 0
while True:
await asyncio.sleep(0)
print('...boo {0}'.format(i))
i += 1
async def say_baa():
i = 0
while True:
await asyncio.sleep(0)
print('...baa {0}'.format(i))
i += 1
# wrap in Task object
# -> automatically attaches to event loop and executes
boo = asyncio.ensure_future(say_boo())
baa = asyncio.ensure_future(say_baa())
loop = asyncio.get_event_loop()
loop.run_forever()
두 개의 루핑 태스크를 동시에 실행하려는 경우 태스크에 내부 await표현식 이 없으면 while루프에 갇혀 다른 태스크가 실행되는 것을 효과적으로 차단합니다 (일반 while루프 와 매우 유사 함 ). 그러나 작업이 (a) 기다리면 문제없이 동시에 실행되는 것처럼 보입니다.
따라서이 await명령문은 작업간에 앞뒤로 전환 할 수있는 발판을 이벤트 루프에 제공하여 동시성의 효과를 제공하는 것 같습니다.
내부 출력 예 await:
running async test
...boo 0
...baa 0
...boo 1
...baa 1
...boo 2
...baa 2
내부가 없는 출력 예 await:
...boo 0
...boo 1
...boo 2
...boo 3
...boo 4
질문
이 구현은 동시 루핑 작업의 '적절한'예를 통과합니까 asyncio?
이것이 작동하는 유일한 방법 은 이벤트 루프가 여러 작업을 처리 Task할 수 있도록 차단 지점 ( await표현식) 을 제공하는 것 뿐 입니까?
예, 이벤트 루프 내에서 실행중인 코 루틴은 다른 코 루틴과 작업이 실행되지 않도록 차단합니다.
yield from또는await(Python 3.5 이상을 사용하는 경우)를 사용 하여 다른 코 루틴을 호출 합니다.- 보고.
이는 asyncio단일 스레드 이기 때문 입니다. 이벤트 루프가 실행되는 유일한 방법은 다른 코 루틴이 능동적으로 실행되지 않는 것입니다. yield from/ 사용 await은 코 루틴을 일시적으로 중단하여 이벤트 루프가 작동 할 수있는 기회를 제공합니다.
Your example code is fine, but in many cases, you probably wouldn't want long-running code that isn't doing asynchronous I/O running inside the event loop to begin with. In those cases, it often makes more sense to use BaseEventLoop.run_in_executor to run the code in a background thread or process. ProcessPoolExecutor would be the better choice if your task is CPU-bound, ThreadPoolExecutor would be used if you need to do some I/O that isn't asyncio-friendly.
Your two loops, for example, are completely CPU-bound and don't share any state, so the best performance would come from using ProcessPoolExecutor to run each loop in parallel across CPUs:
import asyncio
from concurrent.futures import ProcessPoolExecutor
print('running async test')
def say_boo():
i = 0
while True:
print('...boo {0}'.format(i))
i += 1
def say_baa():
i = 0
while True:
print('...baa {0}'.format(i))
i += 1
if __name__ == "__main__":
executor = ProcessPoolExecutor(2)
loop = asyncio.get_event_loop()
boo = asyncio.ensure_future(loop.run_in_executor(executor, say_boo))
baa = asyncio.ensure_future(loop.run_in_executor(executor, say_baa))
loop.run_forever()
You don't necessarily need a yield from x to give control over to the event loop.
In your example, I think the proper way would be to do a yield None or equivalently a simple yield, rather than a yield from asyncio.sleep(0.001):
import asyncio
@asyncio.coroutine
def say_boo():
i = 0
while True:
yield None
print("...boo {0}".format(i))
i += 1
@asyncio.coroutine
def say_baa():
i = 0
while True:
yield
print("...baa {0}".format(i))
i += 1
boo_task = asyncio.async(say_boo())
baa_task = asyncio.async(say_baa())
loop = asyncio.get_event_loop()
loop.run_forever()
Coroutines are just plain old Python generators. Internally, the asyncio event loop keeps a record of these generators and calls gen.send() on each of them one by one in a never ending loop. Whenever you yield, the call to gen.send() completes and the loop can move on. (I'm simplifying it; take a look around https://hg.python.org/cpython/file/3.4/Lib/asyncio/tasks.py#l265 for the actual code)
That said, I would still go the run_in_executor route if you need to do CPU intensive computation without sharing data.
'program story' 카테고리의 다른 글
| JAX-RS에 대한 @PATCH 어노테이션을 갖는 방법은 무엇입니까? (0) | 2020.11.21 |
|---|---|
| console.log 대신 angular의 $ log를 사용하는 이유는 무엇입니까? (0) | 2020.11.21 |
| RecyclerView.Adapter의 onBindViewHolder 내에 OnClickListener를 추가하는 것이 왜 나쁜 습관으로 간주됩니까? (0) | 2020.11.21 |
| C ++ 17의 새로운 범위 기반 for 루프가 Ranges TS에 어떻게 도움이 되나요? (0) | 2020.11.21 |
| Visual Studio : "다른 네임 스페이스로 클래스 이동"리팩토링이 있습니까? (0) | 2020.11.21 |