Python의 asyncio
라이브러리는 비동기 프로그래밍을 위해 제공되는 표준 라이브러리입니다. 비동기 프로그래밍은 주로 I/O 바운드 작업(예: 네트워크 요청, 파일 읽기/쓰기 등)에서 효율성을 높이기 위해 사용됩니다. asyncio
를 사용하면 프로그램이 블로킹 없이 동시에 여러 작업을 수행할 수 있습니다.
기본 개념
- 코루틴 (Coroutine): 비동기 함수로,
async def
로 정의됩니다. 코루틴은await
키워드로 다른 코루틴이나 비동기 함수의 실행을 일시 중지하고 제어를 이벤트 루프로 반환할 수 있습니다. - 이벤트 루프 (Event Loop): 비동기 작업을 관리하고 실행하는 핵심 구성 요소입니다. 이벤트 루프는 등록된 모든 코루틴과 비동기 작업을 순차적으로 실행합니다.
- 태스크 (Task): 이벤트 루프에서 실행되는 코루틴입니다. 태스크는 코루틴의 실행을 관리합니다.
- 퓨처 (Future): 비동기 작업의 최종 결과를 나타내는 객체입니다.
기본 사용법
1. 코루틴 정의 및 실행
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1)
print("World")
asyncio.run(say_hello())
async def
로 코루틴을 정의합니다.await
를 사용하여 비동기 함수의 실행을 일시 중지합니다.asyncio.run
을 사용하여 코루틴을 실행합니다.
2. 여러 코루틴 실행
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1)
print("World")
async def main():
await asyncio.gather(
say_hello(),
say_hello()
)
asyncio.run(main())
asyncio.gather
를 사용하여 여러 코루틴을 동시에 실행할 수 있습니다.
3. 태스크 만들기
import asyncio
async def say_hello():
print("Hello")
await asyncio.sleep(1)
print("World")
async def main():
task1 = asyncio.create_task(say_hello())
task2 = asyncio.create_task(say_hello())
await task1
await task2
asyncio.run(main())
asyncio.create_task
를 사용하여 코루틴을 태스크로 만들어 실행합니다.
4. 비동기 I/O 작업
import asyncio
async def read_file():
await asyncio.sleep(1) # 파일을 읽는 대신 대기
print("File read complete")
async def main():
await read_file()
asyncio.run(main())
실용 예제
비동기 TCP 서버
import asyncio
async def handle_client(reader, writer):
addr = writer.get_extra_info('peername')
print(f"Connected to {addr}")
while True:
data = await reader.read(100)
if not data:
break
message = data.decode()
print(f"Received {message} from {addr}")
writer.write(data)
await writer.drain()
print(f"Disconnected from {addr}")
writer.close()
await writer.wait_closed()
async def main():
server = await asyncio.start_server(handle_client, '127.0.0.1', 8888)
async with server:
await server.serve_forever()
asyncio.run(main())
비동기 TCP 클라이언트
import asyncio
async def tcp_client():
reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
message = 'Hello, World!'
print(f'Send: {message}')
writer.write(message.encode())
await writer.drain()
data = await reader.read(100)
print(f'Received: {data.decode()}')
print('Close the connection')
writer.close()
await writer.wait_closed()
asyncio.run(tcp_client())
주요 함수 및 메서드
asyncio.run(coroutine)
: 이벤트 루프를 시작하고 주어진 코루틴을 실행합니다.asyncio.create_task(coroutine)
: 코루틴을 태스크로 만들어 이벤트 루프에서 실행합니다.asyncio.gather(*coroutines)
: 여러 코루틴을 동시에 실행하고 결과를 수집합니다.asyncio.sleep(seconds)
: 주어진 시간 동안 비동기적으로 대기합니다.asyncio.start_server()
: 비동기 TCP 서버를 시작합니다.asyncio.open_connection()
: 비동기 TCP 클라이언트를 시작합니다.
asyncio
는 효율적인 비동기 프로그래밍을 위한 강력한 도구입니다. 이를 사용하여 네트워크 서버, 클라이언트, 파일 I/O 등을 비동기적으로 처리할 수 있습니다.
asyncio.gather
와 asyncio.create_task
는 모두 여러 코루틴을 동시에 실행하기 위한 방법이지만, 두 방법에는 중요한 차이점이 있습니다.
asyncio.gather
asyncio.gather
는 주어진 코루틴들을 동시에 실행하고, 모든 코루틴이 완료되면 결과를 반환합니다. asyncio.gather
를 사용하면 각 코루틴의 결과를 모아서 반환하기 때문에, 각 코루틴의 결과를 다루기 쉽습니다.
import asyncio
async def task1():
await asyncio.sleep(1)
return 'Task 1 completed'
async def task2():
await asyncio.sleep(2)
return 'Task 2 completed'
async def main():
results = await asyncio.gather(task1(), task2())
print(results)
asyncio.run(main())
위 예제에서 task1
과 task2
는 동시에 실행됩니다. asyncio.gather
는 두 코루틴이 모두 완료될 때까지 기다렸다가 결과 리스트를 반환합니다.
asyncio.create_task
asyncio.create_task
는 코루틴을 Task
객체로 감싸서 바로 실행을 시작합니다. create_task
는 즉시 실행을 시작하지만, 결과를 얻으려면 태스크를 await
해야 합니다.
import asyncio
async def task1():
await asyncio.sleep(1)
return 'Task 1 completed'
async def task2():
await asyncio.sleep(2)
return 'Task 2 completed'
async def main():
t1 = asyncio.create_task(task1())
t2 = asyncio.create_task(task2())
await t1
await t2
print(t1.result(), t2.result())
asyncio.run(main())
위 예제에서 task1
과 task2
는 create_task
를 통해 태스크로 만들어져 즉시 실행됩니다. await
키워드를 사용하여 각각의 태스크가 완료될 때까지 기다린 후 결과를 얻습니다.
차이점 요약
- 결과 관리:
asyncio.gather
는 모든 코루틴의 결과를 한 번에 반환하므로, 결과를 관리하기 쉽습니다.asyncio.create_task
는 각 태스크의 결과를 개별적으로 관리해야 합니다.
- 예외 처리:
asyncio.gather
는 실행된 모든 코루틴 중 하나라도 예외가 발생하면, 나머지 코루틴들이 완료되기 전에 예외를 발생시킵니다.asyncio.create_task
를 사용하면 개별 태스크에서 예외가 발생할 수 있으며, 각 태스크에서 발생한 예외를 개별적으로 처리할 수 있습니다.
- 제어권:
asyncio.gather
는 모든 코루틴이 완료될 때까지 기다립니다.asyncio.create_task
는 태스크를 실행 상태로 만들고, 나중에 원하는 시점에await
할 수 있습니다. 즉, 더 많은 제어권을 제공합니다.
예제: 예외 처리 비교
asyncio.gather
예외 처리
import asyncio
async def task1():
await asyncio.sleep(1)
return 'Task 1 completed'
async def task2():
await asyncio.sleep(2)
raise ValueError('An error occurred in task2')
async def main():
try:
results = await asyncio.gather(task1(), task2())
except Exception as e:
print(f'Exception caught: {e}')
asyncio.run(main())
asyncio.create_task
예외 처리
import asyncio
async def task1():
await asyncio.sleep(1)
return 'Task 1 completed'
async def task2():
await asyncio.sleep(2)
raise ValueError('An error occurred in task2')
async def main():
t1 = asyncio.create_task(task1())
t2 = asyncio.create_task(task2())
await t1
try:
await t2
except Exception as e:
print(f'Exception caught: {e}')
asyncio.run(main())
위 예제에서 asyncio.gather
는 task2
에서 예외가 발생하면 즉시 예외를 발생시킵니다. 반면, asyncio.create_task
를 사용하면 개별적으로 예외를 처리할 수 있습니다.
결론
asyncio.gather
는 여러 코루틴의 결과를 한 번에 처리하고 싶을 때 유용합니다.asyncio.create_task
는 더 많은 제어권과 유연성을 제공하며, 개별 태스크를 관리하고 예외를 개별적으로 처리할 때 유용합니다.
이 두 가지 방법을 상황에 맞게 사용하면 비동기 프로그래밍을 더 효과적으로 관리할 수 있습니다.
'대동단결 Python' 카테고리의 다른 글
통신을 위한 바이트 편집에 관한 python (0) | 2024.09.03 |
---|---|
JSON으로 직렬화할 수 있는 Python 데이터 타입 (0) | 2024.07.12 |
모듈 일괄 설치 (1) | 2023.12.19 |
라즈베리파이 파이썬 업그레이드 (0) | 2023.07.27 |
파이썬의 데코레이션 이란? (0) | 2023.02.04 |
댓글