[Python] 비동기 프로그래밍 정리 5 (loop.run_in_executor)

hodu·2022년 11월 14일
1

asyncio

목록 보기
5/7

아래 링크를 읽고 정리한 내용입니다.

🚨 더 구체적인 내용은 아래 링크에 아주 친절하게 잘 정리되어 있으니 꼭 링크가서 읽어주세요!!
https://it-eldorado.tistory.com/159?category=749661


동기 함수를 코루틴처럼 쓰는 방법

대부분의 API는 비동기를 지원하지 않는 "동기"방식으로 동작한다.
왜냐, python 자체가 동기 방식으로 동작하도록 설계된 언어이기 때문이다.

실제 asyncio.sleep()이 지원되기 이전에는 동기 함수인 time.sleep()만 사용할 수 있었는데 time.sleep()은 실행 흐름을 블록하는 블로킹 함수이다.

마찬가지로 request.get(), request.post() 도 현재 실행을 블록하는 함수이다.
이러한 함수"만" 이용하면 결국 asyncio를 사용하게 되는 이유 자체가 명확하지 않게 된다. 비동기 프로그래밍이 가능하려면 그 작업을 다른 곳에 맡겨두고 Future객체를 await 하면서 실행중인 태스크의 제어를 이벤트 루프에게 넘겨주어야 하기 때문이다.

이때 사용하는 것이 loop.run_in_executor() 메서드인데 loop는 이벤트 루프객체를 의미한다.
간단하게 얘기하자면 이 메서드는 동기 함수를 "별도의 쓰레드"에서 실행하기 때문에 비동기 함수처럼 사용할 수 있도록 해준다.

비동기 프로그래밍은 작업을 다른 어딘가(별도의 쓰레드)에 맡기는 것을 의미한다.

이 함수의 사용 방법을 알아보자. 이 함수의 반환 값은 퓨처 객체이기 때문에 await 키워드 뒤에 올 수 있다.

import asyncio
import time

async def sleep(sec):
    await loop.run_in_executor(None, time.sleep, sec)  # time.sleep(sec)
    return sec

async def main():
    sec_list = [1, 2]
    tasks = [asyncio.create_task(sleep(sec)) for sec in sec_list]  # [Task 1 객체, Task 2 객체]
    tasks_results = await asyncio.gather(*tasks)  # [Task 1 객체의 결과 값, Task 2 객체의 결과 값]
    return tasks_results

start = time.time()

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

end = time.time()

print('result : {}'.format(result))
print('total time : {0:.2f} sec'.format(end - start))

# 출력 결과
# result : [1, 2]
# total time : 2.03 sec

원래는 블로킹 함수인 time.sleep() 함수가 마치 asyncio.sleep() 함수처럼 동작할 수 있도록 하였다.

loop.run_in_executor() 메소드의 첫 번째 인자로 넘어가는 None은 실행기를 명시적으로 지정하지 않고 기본 실행기를 사용하겠다는 것인데, 직접 실행기를 지정하면 워커 쓰레드를 원하는 개수만큼 생성하는 것이 가능하다.

두 번째 인자에는 함수 이름을 넘기고, 세 번째 인자부터는 그 함수를 호출할 때 넘길 인자들을 하나씩 넘기면 된다.

profile
안녕 세계!

0개의 댓글