파이썬 코루틴과 비동기 함수

이상민·2023년 7월 12일
2

CPU 바운드 & I/O 바운드

바운드는 크게 두가지 cpu, i/o 바운드로 나뉜다.

  • CPU 바운드

    프로그램이 실행될 때 실행 속도가 cpu 속도에 의해 제한됨을 의미한다. cpu가 계속 계산을하기 때문에 cpu에서 계산이 끝날때 까지 프로그램이 잠시 멈춘다.

  • I/O 바운드

    I,O는 각각 inputoutput을 의미하며, 프로그램이 실행될 때 실행 속도가 I/O에 의해 제한됨을 의미한다. 사용자가 숫자를 입력하는 경우 뿐만 아니라, 컴퓨터끼리의 통신을 할 때에도 I/O 바운드가 발생

# 예시코드 1. 사용자 I/O
import time

def  io_bound_func():
    print("값을 입력해주세요.")
    input_value = input()
    return int(input_value)*100


if __name__ == "__main__":

    start_time = time.time()
    result = io_bound_func()
    end_time = time.time()
    print(result)
    print("실행시간:{} ".format(end_time - start_time))

#예시코드 2. 서버 통신시 I/O 바운드
import requests

def  io_bound_func():
    result = requests.get("https://google.com")
    return result

if __name__ == "__main__":

    start_time = time.time()
    result = io_bound_func()
    end_time = time.time()
    print(result)
    print("실행시간:{} ".format(end_time - start_time))

블로킹

바운드에 의해 코드가 멈추게 되는 현상이 일어나는 것

  • 예시코드
def  io_bound_func():
    result = requests.get("https://google.com")
    return result

if __name__ == "__main__":

    start_time = time.time()
    for i in range(10):
    	result = io_bound_func()
    end_time = time.time()
    print(result)
    print("실행시간:{} ".format(end_time - start_time))

위 코드를 보면 총 10번의 블로킹이 발생한 것을 볼 수 있다.

동기 & 비동기

  • 동기(Snyc): 직렬작업을 수행하는 것, 예를 들어 코드가 동기적으로 동작한다는 것은 코드가 반드시 작성한 순서 그대로 실행 된다는 것이다.
  • 예시코드
    A,B,C 고객에게 짜장면을 배달한 뒤 고객이 다먹을 때까지 기다린 뒤, 그릇을 수거하고 다음 고객에게 배달하는 작업
import time 

def delivery(name, mealtime):
    print(f"{name}에게 배달 완료.")
    time.sleep(mealtime)
    print(f"{name} 식사완료, {mealtime}시간 소요...")
    print(f"{name} 그릇 수거 완료")

def main():
    delivery("A",3)
    delivery("B",3)
    delivery("C",3)

if __name__ == "__main__":
    srt = time.time()
    main()
    end = time.time()
    print(f"소요시간: {end -srt}")
  • 결과
  • 비동기(Asnyc): 앞선 예와 반대로 비동기적으로 수행한다는 것은 코드가 작성된 순서대로 수행 되는 것이 아닌
  • 코드예시

import asyncio

async def delivery(name, mealtime):
    print(f"{name}에게 배달 완료.")
    # await time.time()을 사용하면 time은 awaitable함수가 아니므로 에러가 발생
    await asyncio.sleep(mealtime) #await을 기점으로 다른 코루틴 함수로 넘어간다
    print(f"{name} 식사완료, {mealtime}시간 소요...")
    print(f"{name} 그릇 수거 완료")

async def main():
	#asyncio.gather: 동시에 태스크 실행 (동시성) - 단 병렬로 처리 되는 것은 아님 
    await asyncio.gather( 
    delivery("A",5),
    delivery("B",3),
    delivery("C",2),
    )

if __name__ == "__main__":
    srt = time.time()
    asyncio.run(main()) #asyncio.run(): 코루틴 함수를 실행하는 함수 
    end = time.time()
    print(f"소요시간: {end -srt}")
  • 결과

  • 동기방식
    delivery_func(A)가 종료되기 전까지 다음 함수를 호출하지 않고 기다린다.

  • 비동기 방식

코루틴 이해

  • 서브루틴의 일반화 된 상태

  • 다양한 진입점과 다양한 탈출 점이 있는 루틴 아래 예시코드를 보면 delivery 루트는 awaitreturn으로 해단 루틴을 탈출할 수 있다. 물론 await기점으로 다시 진입 가능

  • 파이썬 비동기 함수코루틴 함수로 만들 수 있다.

async def delivery(name, mealtime):
    print(f"{name}에게 배달 완료.")
    await asyncio.sleep(mealtime)
    print(f"{name} 식사완료, {mealtime}시간 소요...")
    print(f"{name} 그릇 수거 완료")
    
    return
async def main():
    await asyncio.gather(
    delivery("A",5),
    delivery("B",3),
    delivery("C",2),
    )
    
main()

코루틴 활용

requests를 활용한 동기화 코드

"""
코루틴 활용

네이버 웹 데이터 가져오기

"""
import requests
import time

def fetcher(session, url):
    with session.get(url) as response:
        return response.text

def main():
    urls = ["https://naver.com","https://google.com","https://instagram.com"]*10
    with requests.Session() as session:
        result = [fetcher(session,url) for url in urls]
        print(result)
if __name__ == "__main__":
    start = time.time()
    main()
    end = time.time()
    print(f"소요시간: {end- start} 초") #10.17

소요시간: 10.467732906341553 초

aiohttp를 활용한 비동기 코드

import requests
import time
import aiohttp
import asyncio

async def fetcher(session, url):
    async with session.get(url) as response:
        return await response.text() #response는 awaitable 객체

async def main():
    urls = ["https://naver.com","https://google.com","https://instagram.com"]*10


    async with aiohttp.ClientSession() as session:
        result = await asyncio.gather(*[fetcher(session,url) for url in urls]) #result에는 urls 내부의 주소의 정보가 순서대로 담겨있다
        print(len(result))

if __name__ == "__main__":
    start = time.time()
    asyncio.run(main())
    end = time.time()
    print(f"소요시간: {end- start} 초") #0.84초
소요시간:  0.8458449840545654 초
profile
잘하자

0개의 댓글