파이썬 비동기 프로그래밍
포스트
취소

파이썬 비동기 프로그래밍

동기 vs 비동기 코드 비교

  • 동기 프로그래밍

    1. 코드

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      
      import requests
      import time
      
      
      def get_google_response():
          start = time.time()
          res = requests.get('https://www.google.com')
          return res.status_code, time.time() - start
      
      
      def normal_code():
          start = time.time()
          for _ in range(20):
              status_code, taken_time = get_google_response()
              print(status_code, ': ', taken_time)
          print('total: {}'.format(time.time() - start))
      
      
      if __name__ == '__main__':
          normal_code()
      
    2. 결과

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
      200 :  0.3122532367706299
      200 :  0.34461331367492676
      200 :  0.36156463623046875
      200 :  0.3558688163757324
      200 :  0.35318613052368164
      200 :  0.37262439727783203
      200 :  0.3737161159515381
      200 :  0.3346893787384033
      200 :  0.39562082290649414
      200 :  0.35920214653015137
      200 :  0.35454726219177246
      200 :  0.35430455207824707
      200 :  0.3461928367614746
      200 :  0.3401949405670166
      200 :  0.3195078372955322
      200 :  0.3500397205352783
      200 :  0.3499133586883545
      200 :  0.32646942138671875
      200 :  0.34032320976257324
      200 :  0.3709850311279297
      total: 7.015817165374756
      
  • 비동기 프로그래밍

    1. 코드

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      
      import requests
      import time
      import asyncio
      
      
      async def get_google_response(loop):
          start = time.time()
          res = await loop.run_in_executor(None, requests.get, 'https://www.google.com')
          return res.status_code, time.time() - start
      
      
      def async_code():
          start = time.time()
          loop = asyncio.get_event_loop()
          tasks = [get_google_response(loop) for _ in range(20)]
          completed_tasks, _ = loop.run_until_complete(asyncio.wait(tasks))
          for done in completed_tasks:
              status_code, taken_time = done.result()
              print(status_code, ': ', taken_time)
          print('total: {}'.format(time.time() - start))
          loop.close()
      
      
      if __name__ == '__main__':
          async_code()
      
    2. 결과

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
      200 :  0.29973459243774414
      200 :  0.2707829475402832
      200 :  0.2817347049713135
      200 :  0.32273435592651367
      200 :  0.31773948669433594
      200 :  0.3037395477294922
      200 :  0.5270824432373047
      200 :  0.32169365882873535
      200 :  0.3247337341308594
      200 :  0.489962100982666
      200 :  0.2887296676635742
      200 :  0.5300889015197754
      200 :  0.31470251083374023
      200 :  0.2817227840423584
      200 :  0.5450804233551025
      200 :  0.31772708892822266
      200 :  0.2817366123199463
      200 :  0.2977299690246582
      200 :  0.29676270484924316
      200 :  0.3117361068725586
      total: 0.5750386714935303
      

처리 결과를 기다리기 싫다.

위 코드는 구글 사이트에 요청을 보내 응답을 받는 것을 20번 반복해 각 요청-응답 시간과 총 소요시간을 출력하는 코드다.

동기식으로 처리했을 때는 하나의 요청-응답이 끝나야 다음 요청-응답을 진행하기 때문에 총 소요시간이 모든 작업 시간을 합한 값과 비슷하게 나왔다.

하지만 비동기식으로 처리했을 때는 각 요청 작업이 응답을 기다리지 않고 바로 다음 요청 작업을 수행하기 때문에 총 소요시간이 확연하게 줄어든 것을 볼 수 있다.

코루틴

1
2
3
4
async def get_google_response(loop):
    start = time.time()
    res = await loop.run_in_executor(None, requests.get, 'https://www.google.com')
    return res.status_code, time.time() - start

파이썬에서 비동기식 프로그래밍을 할 때는 함수 앞에 async를 붙인다. 그래야 이 함수가 코루틴이 된다.

원래 파이썬은 지원하는 특정 함수만 코루틴으로써 사용할 수 있다. 코루틴이 아닌 일반 함수를 코루틴처럼 실행할 수 있게 만드는 것이 loop.run_in_executor() 이다.

1
2
3
4
loop = asyncio.get_event_loop()
tasks = [get_google_response(loop) for _ in range(20)]
completed_tasks, _ = loop.run_until_complete(asyncio.wait(tasks))
loop.close

비동기식 작업에는 기다림이 없고 막히면 다음 작업을 실행한다. 해당 코드는 그런 루프를 만드는 과정이다.

키워드를 요구하는 매개변수를 전달해야할 때는?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import requests
import time
import asyncio


async def get_google_response(loop, timeout, stream):
    start = time.time()

    def requests_wrapper():
        return requests.get('https://www.google.com', timeout=timeout, stream=stream)

    res = await loop.run_in_executor(None, requests_wrapper)
    return res.status_code, time.time() - start


def async_code():
    start = time.time()
    loop = asyncio.get_event_loop()
    tasks = [get_google_response(loop, timeout=0.5, stream=True) for _ in range(20)]
    completed_tasks, _ = loop.run_until_complete(asyncio.wait(tasks))
    for done in completed_tasks:
        status_code, taken_time = done.result()
        print(status_code, ': ', taken_time)
    print('total: {}'.format(time.time() - start))
    loop.close()


if __name__ == '__main__':
    async_code()

코루틴에 wrapper 함수를 내장해 키워드 매개변수를 전달해주면 된다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.