알 수 없는 사용자 2022. 6. 15. 14:09

Asyncio

단일 thread 에 마치 multi tasking을 하는 것과 유사한 기능을 수행할 수 있게 된다.

이를 이해하기 위하여는 먼저 기존 python의 thread나 process 에서의 concurrent 프로그래밍 방식으로 python 3.2에 추가된 future (PEP 3148 – futures - execute computations asynchronously) 를 이해할 필요가 있다. 이후에 만들어진 asyncio도 이와 동일한 API로 만들어진 것이다.

Future는 쉽게 말해서 work thread(process)의 핸들이라고 볼수 있다. 이를 future.result()와 같이 종료가 끝날때 까지 기다리게 되면, 해당 work funtion에서 결과를 완료하거나, exception이 발생한 경우 이를 받을 수 있다.

future

urlopen 이라는 함수는 blocking 함수임. 따라서 URL 1개 읽을 때마다 2초가 걸린다고 치면 총 6초가 걸림.

그러나 future핸들링을 이용해 ThreadPool을 돌린다면 총 2초 정도가 나온다.

from concurrent import futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://some-made-up-domain.com/']

def load_url(url, timeout):
    return urllib.request.urlopen(url, timeout=timeout).read()

def main():
    with futures.ThreadPoolExecutor(max_workers=5) as executor:
        future_to_url = dict(
            (executor.submit(load_url, url, 60), url)
             for url in URLS)

        for future in futures.as_completed(future_to_url):
            url = future_to_url[future]
            try:
                print('%r page is %d bytes' % (
                          url, len(future.result())))
            except Exception as e:
                print('%r generated an exception: %s' % (
                          url, e))

if __name__ == '__main__':
    main()

 

  • excutor.submit()으로 thread pool에서 돌릴 함수를 등록하면 future를 리턴한다. 등록된 함수는 thread pool에서 비동기로 실행된다.
    # excutor.submit() 인자로 쓰레드돌릴 타겟과 인자를 보내줌
  • futures.as_completed() 처럼 결과가 완료된 순서되로 리턴되는 generator를 리턴 받을 수 있다. 위와 같이 for..in 에 넣어 loop를 돌릴 수 있다. 완료되거나 비정상 종료된 future가 차례대로 나오게 된다.
    # futures.as_completed() 으로 각각의 쓰레드 종료 상태 확인 + return값으로 future인스턴스의 이터레이터를 반환합니다. // 여기선 executor의 인스턴스(iterable)인 url
  • future.result() 로 결과를 받을 수 있다. 만일 future내의 함수(load_url())에서 exception이 발생한 것도 future를 통하여 호출한 thread에서 받을 수 있게 된다. 위 예제와 같이 future.result()가 try..except 문으로 감싸서 해당 작업에서 발생한 예외도 받을 수 있다.
    # future.result() 콜한 함수의 return 값을 반환 // 여기선 load_url의 return 값