asyncio(Asynchronous I/O)는 비동기 프로그래밍을 위한 모듈

-> CPU와 I/O를 병렬로 처리하게 함

 

동기(synchronous) 처리는 특정 작업이 끝나면 다음 작업을 처리하는 순차처리 방식

비동기(asynchronous) 처리는 여러 작업을 처리하도록 예약한 뒤작업이 끝나면 결과를 받는 방식

 

asyncio 사용법

- async def 는 python3.5 이상 부터 가능

- 비동기 함수를 python에서는 코루틴(coroutine)이라고 부름

async def do_async():
    pass
    
 # do_async() # <coroutine object do_async at 0x1038de710>

비동기 함수는 일반적으로 async로 선언된 다른 비동기 함수 내에서 await 키워드를 붙여 호출함

 

async로 선언되지 않은 일반 동기 함수 내에서 비동기 함수를 호출하려면 asyncio라이브러리의 이벤트 루프를 사용

loop = asyncio.get_event_loop()
loop.run_until_complete(main_async())
loop.close()

 

예시1)

import asyncio
 
async def hello():    # async def로 네이티브 코루틴을 만듦
    print('Hello, world!')
 
loop = asyncio.get_event_loop()     # 이벤트 루프를 얻음
loop.run_until_complete(hello())    # hello가 끝날 때까지 기다림
loop.close()                        # 이벤트 루프를 닫음

 

await 사용법

- await는 python3.5 이상 부터 가능

- await 뒤에 코리틴 객체/퓨처 객체/태스크 객체를 지정

- 해당 객체가 끝날 때 까지 기다린뒤 결과를 반환

- 특정 객체가 끝날 때까지 기다림

- await는 네이티브 코루틴 안에서만 사용 가능

 

import asyncio
 
async def add(a, b):
    print('add: {0} + {1}'.format(a, b))
    await asyncio.sleep(1.0)    # 1초 대기. asyncio.sleep도 네이티브 코루틴
    return a + b    # 두 수를 더한 결과 반환
 
async def print_add(a, b):
    result = await add(a, b)    # await로 다른 네이티브 코루틴 실행하고 반환값을 변수에 저장
    print('print_add: {0} + {1} = {2}'.format(a, b, result))
 
loop = asyncio.get_event_loop()             # 이벤트 루프를 얻음
loop.run_until_complete(print_add(1, 2))    # print_add가 끝날 때까지 이벤트 루프를 실행
loop.close()                                # 이벤트 루프를 닫음

결과

add: 1 + 2
print_add: 1 + 2 = 3

 

퓨처(asyncio.Future)는 미래에 할 일을 표현하는 클래스인데 할 일을 취소하거나 상태 확인, 완료 및 결과 설정에 사용

태스크(asyncio.Task)는 asyncio.Future의 파생 클래스이며 asyncio.Future의 기능과 실행할 코루틴의 객체를 포함하고, 코루틴의 실행을 취소하거나 상태 확인, 완료 및 결과 설정에 사용

 


비동기로 웹 페이지 가져오기

1. 일반

from time import time
from urllib.request import Request, urlopen
 
urls = ['https://www.google.co.kr/search?q=' + i
        for i in ['apple', 'pear', 'grape', 'pineapple', 'orange', 'strawberry']]
 
begin = time()
result = []
for url in urls:
    request = Request(url, headers={'User-Agent': 'Mozilla/5.0'})    # UA가 없으면 403 에러 발생
    response = urlopen(request)
    page = response.read()
    result.append(len(page))
 
print(result)
end = time()
print('실행 시간: {0:.3f}초'.format(end - begin))

- 결과

[89590, 88723, 88802, 90142, 90628, 92663]
실행 시간: 8.422초

 

2. asyncio

from time import time
from urllib.request import Request, urlopen
import asyncio
 
urls = ['https://www.google.co.kr/search?q=' + i
        for i in ['apple', 'pear', 'grape', 'pineapple', 'orange', 'strawberry']]
 
async def fetch(url):
    request = Request(url, headers={'User-Agent': 'Mozilla/5.0'})    # UA가 없으면 403 에러 발생
    response = await loop.run_in_executor(None, urlopen, request)    # run_in_executor 사용
    page = await loop.run_in_executor(None, response.read)           # run in executor 사용
    return len(page)
 
async def main():
    futures = [asyncio.ensure_future(fetch(url)) for url in urls]
                                                           # 태스크(퓨처) 객체를 리스트로 만듦
    result = await asyncio.gather(*futures)                # 결과를 한꺼번에 가져옴
    print(result)
 
begin = time()
loop = asyncio.get_event_loop()          # 이벤트 루프를 얻음
loop.run_until_complete(main())          # main이 끝날 때까지 기다림
loop.close()                             # 이벤트 루프를 닫음
end = time()
print('실행 시간: {0:.3f}초'.format(end - begin))

- 결과

[89556, 88682, 89925, 90164, 90513, 93965]
실행 시간: 1.737초

 

해석

-  urlopen이나 response같은 메서드는 결과가 나올 때까지 코드 실행이 중단 됨 

   이러한 함수를 blocking I/O 함수라고 부름

반응형
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기