[Python] 비동기 프로그래밍 정리 1-1 (앙꼬있는 찐빵 만들기)

hodu·2022년 11월 18일
0

asyncio

목록 보기
6/7

이전 글 마지막 코드를 보면 비동기 함수를 만들었지만 비동기적으로 실행되지 않았다.

이번 글에서는 앙꼬있는 찐빵, 초코있는 초코칩 쿠키를 만들어보겠다.

다시 비동기

import asyncio, time

async def greeting(delay, print_string):
	await asyncio.sleep(delay)
    print(print_string)
    
async def main():
	print(f'시작 시간 -> {time.ctime()}')
   	await greeting(1, 'Hello!')       # 2
    await greeting(2, 'World!')       # 2
    print(f'끝난 시간 -> {time.ctime()}')
    
asyncio.run(main())   

위 코드는 asyncawait를 이용해서 뭔가 비동기적인 코드를 만든 것 같지만, 까보면 그렇지 않다.

# 결과
시작 시간 -> 22:45:32
hello
world
끝난 시간 -> 22:45:35

위 함수를 실행해보면 결과값이 3초가 나오는 것을 확인할 수 있을 것이다.

왜일까?

이유는 비동기 작업 객체를 만들어주지 않았다.
asyncawait만을 이용해서는 비동기적인 동작을 하지 않는다. TaskFuture를 이용해서 "비동기 작업객체"를 만들고 그걸 실행해야 비동기적인 코드를 실행할 수 있다.

그럼 진짜로 비동기적인 코드를 만들어보자.

삽질하기

아래 코드는 비동기적으로 동작하지 않는다.

import asyncio, time

async def greeting(delay, print_string):
    await asyncio.sleep(delay)
    print(print_string)

async def main():
    print(f'시작 시간 -> {time.ctime()}')
    await greeting(1, 'Hello!')       # 2
    await greeting(2, 'World!')       # 2
    print(f'끝난 시간 -> {time.ctime()}')

loop = asyncio.get_event_loop()
task = loop.create_task(main())
loop.run_until_complete(task)

pending = asyncio.all_tasks(loop=loop)
for task in pending:
    task.cancel()

group = asyncio.gather(*pending, return_exceptions=True)
loop.run_until_complete(group)
loop.close()

나는 main 함수만 create_task를 이용하여 비동기 객체로 만들어주면 내부에 있는 greeting함수는 알아서 비동기적으로 실행될 것이라고 생각했다.

하지만 아니었다.

await는 뒤에 적혀져 있는 비동기 함수가 끝날 때까지 기다려주는 키워드로 동기로 실행되든 비동기로 실행되든 관심이 없다.

그러면 아래쪽에서 task = loop.create_task(main()) 이부분을 통해서 비동기적으로 실행되지 않을까?
👉 main만 비동기객체가 된 것이고, 우리가 가장 중요하게 봐야 할 greeting은 비동기 객체로 만들어지지 않았다.

그렇다면 어떻게 해야할까?

greeting을 비동기 객체로 만들어주면 된다!

성공

import asyncio, time

async def greeting(delay, print_string):
    await asyncio.sleep(delay)
    print(print_string)

# 앙꼬없는 찐빵을 앙꼬있는 찐빵으로 만들기 (진짜 비동기 작업객체를 만들어보자!)
async def main():
    print(f'시작 시간 -> {time.ctime()}')
    task1 = loop.create_task(greeting(1, 'Hello!'))
    task2 = loop.create_task(greeting(2, 'World!'))

    await task1
    await task2
    print(f'끝난 시간 -> {time.ctime()}')

loop = asyncio.new_event_loop()
task = loop.create_task(main())

loop.run_until_complete(task)

loop.close()

여기서는 greeting 함수를 모두 "task"로 만들어주었고 만들어준 task를 await하게 만들어서 비동기객체가 끝날 때 까지 기다리게 만들어주었다.

시작 시간 -> 14:21:31 
Hello!
World!
끝난 시간 -> 14:21:33 

그러면 이렇게 실행시간이 2초로 줄어든 것을 확인할 수 있다.

결론은..

"비동기로 동작하고 싶은 객체"를 task(future)로 만들어준다.

profile
안녕 세계!

0개의 댓글