문제점
출력 파일 핸들에 대한 open, close, write 호출이 주 이벤트 루프에서 이뤄진다.
운영체제의 시스템 콜을 사용하므로 이벤트 루프를 상당히 오랫동안 블록할 수 있다.
동시성이 아주 높은 서버에서는 응답 시간이 늘어날 수 있다.
#debug = True라는 파라미터를 asyncio.run함수로 넘기면 문제 발생의 감지 가능
#잘못 작성된 코루틴이 어떤 파일의 어느 줄에 있는지 알아내는 방법을 ㅂ보여줌
import time
async def slow_coroutine():
time.sleep(0.5) #느린 I/O를 시뮬레이션함
asyncio.run(slow_coroutine(), debug = True)
#프로그램의 응답성을 최대로 높이기 위해서 이벤트 루프 안에서 시스템 콜이 이뤄질 잠재적 가능성 최소화
#모든 데이터를 기록하는 새로운 Thread하위 클래스 만들기
from threading import Thread
class WriteThread(Thread):
def __init__(self, output_path):
super().__init__()
self.output_path = output_path
self.output = None
self.loop = asyncio.new_event_loop()
def run(self):
asyncio.set_event_loop(self.loop)
with open(self.output_path, 'wb') as self.output:
self.loop.run_forever()
#맨 마지막에 한 번 더 이벤트 루프를 실행
#다른 이벤트 루프가 stop()에 await하는 경우 해결
self.loop.run_until_complete(asyncio.sleep(0))
#Lock를 사용할 필요가 없어짐
async def real_write(self, data):
self.output.write(data)
async def write(self, data):
coro = self.real_write(data)
future = asyncio.run_coroutine_threadsafe(coro, self.loop)
await asyncio.wrap_future(future)
#stop메서드를 사용하여 작업자 스레드에게 실행 중단
async def real_stop(self):
self.loop.stop()
async def stop(self):
coro = self.real_stop()
future = asyncio.run_coroutine_threadsafe(coro, self.loop)
await asyncio.wrap_future(future)
#with문과 함께 사용할 수 있도록 __aenter__와 __aexit__메서드를 정의
#주 이벤트 루프 스레드를 느리게 만들지 않으면서 제 시간에 시작하고 종료
async def __aenter__(self):
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, self.start)
return self
async def __aexit__(self, *_):
await self.stop()
#WriteThread 클래스 사용 시 run_tasks를 완전히 비동기적인 버전으로 리팩터링
#비동기 버전은 읽기도 좋고, 주 이벤트 루프 스레드의 동작을 느리게하는 시스템 콜을 전혀 실행 안함
def readline(handle):
...
async def tail_async(handle, interval, write_func):
...
async def run_fully_async(handles, interval, output_path):
async with WriteThread(output_path) as output:
tasks = []
for handle in handles:
coro = tail_async(handle, interval, output.write)
task = asyncio.create_task(coro)
tasks.append(task)
await asyncio.gather(*tasks)
시스템 콜(블로킹 I/O와 스레드 시작도 포함)을 코루틴으로 만들면 프로그램의 응답성이 좋아지고 사용자가 느끼는 지연 시간을 줄일 수 있다.
debug = True 파라미터를 asyncio.run에 넘기면 이벤트 루프가 빨리 반응하지 못하게 방해하는 코루틴 식별