[Python] 파이썬 비동기(asyncio)
- -
파이썬 비동기(async, await)
비동기(Asynchronous) 프로그래밍은 컴퓨터 프로그램이 작업을 동시에 수행하고 작업이 완료되지 않았더라도 다른 작업을 계속 수행할 수 있는 프로그래밍 패러다임이다. 이를 통해 I/O 작업, 네트워크 통신, 사용자 입력 처리 및 다른 비동기 작업을 효율적으로 다룰 수 있다.
비동기 프로그래밍은 주로 웹 서버, 웹 클라이언트, 데이터베이스 액세스, GUI 응용 프로그램 및 다른 이벤트 기반 응용 프로그램에서 사용된다. 파이썬은 asyncio 라이브러리를 사용하여 비동기 프로그램을 지원하며, async 및 await 키워드를 통해 비동기 코드를 작성할 수 있다. 이를 통해 I/O 및 네트워크 작업을 더 효율적으로 다룰 수 있다.
asyncio의 주요 메서드
메서드 | 역할 |
create_task(coroutine) | 주어진 코루틴을 비동기 태스크로 만들고 실행 대기열에 추가한다. 이를 사용하여 병렬로 실행하고 싶은 작업을 생성할 수 있다. |
gather(coroutines) | 여러 개의 코루틴을 동시에 실행하고, 모든 결과를 한꺼번에 가져올 수 있다. 병렬 실행에 유용하다. |
sleep(delay) | 주어진 시간(초) 동안 대기하는 비동기 함수이다. 대기 시간 동안 다른 작업을 수행할 수 있다. |
run(main) | 비동기 작업을 실행하는 진입점 함수이다. main 함수를 실행하고 비동기 작업을 시작한다. |
get_event_loop(coroutine, loop = None) | 현재 이벤트 루프 객체를 가져온다. 이벤트 루프는 비동기 작업을 관리하고 스케줄링하는 핵심 역할을 한다. |
ensure_future(coroutine, loop = None) | 주어진 코루틴 비동기 태스크로 만들고, 이를 현재 이벤트 루프에 등록한다. |
Queue | 비동기 큐를 생성하고 사용할 수 있는 클래스로, 작업 간의 메시지 전달 및 동기화에 유용하다. |
Look | 비동기 이벤트를 사용하여 여러 작업이 특정 이벤트를 기다리고 트리거할 수 있다. |
TimeoutError | 비동기 작업이 주어진 시간 내에 완료되지 않을 때 발생하는 예외이다. |
이러한 메서드와 기능은 asyncio를 사용하여 비동기 프로그램을 작성할 때 중요하게 활용된다.
비동기 함수 정의
import asyncio
async def my_async_function():
# 비동기 작업 수행
비동기 함수를 정의하려면 기존 함수를 정의하는 키워드 앞에 async를 붙이면 된다. 그러면 해당 함수는 비동기 처리 되며 파이썬에서 이를 코루틴(coroutine)이라고 부른다.
비동기 함수의 기본 구조
import asyncio
async def my_async_function():
# 비동기 작업 수행
result = await some_async_operation() # some_async_operation() 함수가 실행될 동안 대기
# 결과 처리
return result
async def main():
# 비동기 함수 실행
result = await my_async_function() # my_async_function() 함수가 실행될 동안 대기
# 결과 처리
if __name__ == "__main__":
asyncio.run(main())
- result = await some_async_operation()은 비동기 함수 내에서 await 키워드를 만나면 그전까지의 코드가 실행되고 some_async_operation() 함수를 실행하게 된다. 함수가 실행되는 동안 my_async_function() 함수는 일시정지 하게 된다.
- async def main()은 비동기 작업을 실행하는 진입점 함수를 정의한다. 이 함수에서 비동기 함수를 실행하고 결과를 처리한다.
- asyncio.run(main())은 이벤트 루프를 시작하여 비동기 작업을 실행한다.
비동기 함수는 일반 함수와 달리 async def로 시작하여, 비동기 작업을 수행하기 위해 await 키워드를 사용할 수 있다. 비동기 함수와 이벤트 루프를 사용하면 비동기 작업을 효율적으로 처리할 수 있다.
그렇다면 비동기(Asynchronous)와 동기(Synchronous)의 차이점은 무엇일까?
동기(Synchronous)
import time
def task1():
print('task1이 작업중에 있습니다...')
time.sleep(1)
print('task1 작업이 종료되었습니다.')
def task2():
print('task2가 작업중에 있습니다...')
time.sleep(1)
print('task2 작업이 종료되었습니다.')
def main():
print('main() 함수가 시작되었습니다.')
task1()
task2()
print('main() 함수가 종료되었습니다.')
main()
위의 예제는 동기로 실행되었을 때의 상황이다. main() 함수에서 task1(), task2() 순서로 함수를 호출하면 task1() 함수가 실행되고 종료될 때까지 기다린 후, task2() 함수가 실행되고 종료된다. 즉, 현재 실행되고 있는 함수가 종료되기 전까지 다음 작업을 수행하지 않고 대기하게 된다. 아래 실행 결과를 보면 확인할 수 있다.
main() 함수가 시작되었습니다.
task1이 작업중에 있습니다...
task1 작업이 종료되었습니다.
task2가 작업중에 있습니다...
task2 작업이 종료되었습니다.
main() 함수가 종료되었습니다.
실행 결과
비동기(Asynchronous)
import asyncio
async def task1():
print('task1이 작업중에 있습니다...')
await asyncio.sleep(1)
print('task1 작업이 완료되었습니다.')
async def task2():
print('task2가 작업중에 있습니다...')
await asyncio.sleep(1)
print('task2 작업이 완료되었습니다.')
async def main():
task1 = asyncio.create_task(task1())
task2 = asyncio.create_task(task2())
print('main 작업이 시작되었습니다.')
await asyncio.gather(task1, task2)
print('main 작업이 종료되었습니다.')
if __name__ == "__main__":
asyncio.run(main())
위의 예제는 asyncio.create_task()를 사용하여 task1()과 task2() 함수를 병렬로 실행하고, asyncio.gather()를 사용하여 두 작업을 병렬로 수행하게 된다. 따라서 main() 함수가 시작될 때 task1()과 task2() 함수는 동시에 실행되며 작업이 동시에 진행된다. 하지만 주의할 점은 await asyncio.gather(task1, task2)를 통해 main() 함수는 두 작업이 모두 완료될 때까지 대기하게 된다. 이것은 두 작업이 병렬로 실행되더라도 main() 함수가 대기하므로 main() 함수가 종료되기까지는 두 작업이 모두 완료되어야 한다.
main 작업이 시작되었습니다.
task1이 작업중에 있습니다...
task2가 작업중에 있습니다...
task1 작업이 완료되었습니다.
task2 작업이 완료되었습니다.
main 작업이 종료되었습니다.
실행 결과
비동기 프로그랭 예제
import asyncio
# 비동기 함수 1
async def task1():
print("Task 1 started")
await asyncio.sleep(2) # 2초 동안 대기
print("Task 1 completed")
# 비동기 함수 2
async def task2():
print("Task 2 started")
await asyncio.sleep(1) # 1초 동안 대기
print("Task 2 completed")
async def main():
# 비동기 함수 task1과 task2를 병렬로 실행
await asyncio.gather(task1(), task2())
if __name__ == "__main__":
asyncio.run(main())
- asyncio 모듈을 가져온다.
- task1() 함수와 task2() 함수를 정의한다. 이러한 함수는 비동기 함수로 표시되며 await를 사용하여 비동기 작업을 수행하고 asyncio.sleep() 함수를 사용하여 각 함수에서 대기하는 시간을 설정한다.
- task1() : "Task 1 started"를 출력하고 2초 동안 대기한 후 "Task 1 completed"를 출력한다.
- task2() : "Task 2 started"를 출력하고 1초 동안 대기한 후 "Task 2 completed"를 출력한다.
- main() 함수를 정의한다. 이 함수는 await asyncio.gather(task1(), task2())를 사용하여 task1()과 task2() 함수를 병렬로 실행한다. asyncio.gather()는 여러 비동기 작업을 동시에 실행하고 모든 작업이 완료될 때까지 기다린다.
- if __name__ == "__main__"은 Python 스크립트를 모듈로 사용할 때와 직접 실행할 때의 차이를 구분하기 위해 사용되는 조건이다. asyncio.run() 함수는 비동기 작업의 시작을 의미하며, 작업이 완료되고 결과가 반환되면 해당 작업의 끝을 나타낸다. 이 함수를 통해 간단하게 비동기 작업을 실행하고 관리할 수 있다.
Task 1 started
Task 2 started
Task 2 completed
Task 1 completed
실행 결과
Q. time 모듈에서의 time.sleep()과 모듈의 asyncio.sleep()은 무슨 차이가 있을까?
A. time.sleep()은 동기 대기 함수로 일반적인 동기 코드에서 사용된다. 현재 스레드 또는 프로세스의 실행을 지정된 시간(초) 동안 중지하며, 모든 스레드 또는 프로세스가 대기한다. 따라서 이 함수를 호출하면 다른 동작 중인 프로그램의 실행도 중지된다. 반면, asyncio.sleep()은 다른 비동기 작업을 차단하지 않고 대기할 수 있다. 지정된 시간(초) 동안 현재 작업을 중지하고 다른 작업이 실행되도록 한다. 다른 작업들과 병렬로 실행 가능하며, 비동기 이벤트 루프가 계속 실행된다.
읽어주셔서 감사합니다.
'Programming Language > Python' 카테고리의 다른 글
[Python] 재귀 함수(Recursive Function) (0) | 2023.11.16 |
---|---|
[Python] 파이썬 텍스트를 오디오로 변환하기(gTTS) (0) | 2023.11.14 |
[Python] 파이썬 enumerate (0) | 2023.11.11 |
[Python] 파이썬 제너레이터(Generator) (0) | 2023.11.10 |
[Python] 파이썬 리스트 슬라이싱(List Slicing) (0) | 2023.11.08 |
소중한 공감 감사합니다