- Published on
Asyncio trong Python
- Authors
- Name
- Hoàng Hữu Mạnh
Asyncio là một thư viện để lập trình bất đồng bộ (asynchronous programming) trong Python. Nó cung cấp các cơ chế để viết code bất đồng bộ một cách đơn giản và trực quan.
Các khái niệm chính trong Asyncio:
Coroutines: Các hàm bất đồng bộ. Chúng có thể tạm dừng và nhường quyền điều khiển cho hàm khác mà không bị block thread chính.
Event loops: Vòng lặp xử lý các coroutines và callbacks. Nó quản lý và điều phối các tác vụ bất đồng bộ.
Futures & Tasks: Đại diện cho kết quả trong tương lai của một coroutine. Task bao bọc coroutine và trả về Future.
Cách sử dụng Asyncio:
Định nghĩa các coroutines với từ khóa async def
Tạo ra một event loop:
import asyncio
loop = asyncio.get_event_loop()
- Chạy các coroutines trong event loop bằng asyncio.run() hoặc loop.run_until_complete():
async def main():
print('hello')
await some_coroutine()
asyncio.run(main())
- Sử dụng await để chờ đợi kết quả từ các hàm bất đồng bộ khác
Ưu điểm của Asyncio:
Code đơn giản, dễ đọc hiểu
Hiệu năng cao với I/O bound tasks
Tiết kiệm tài nguyên so với multi-threading
Nhược điểm:
Khó debug và xử lý lỗi
Không phù hợp cho CPU-bound tasks
Yêu cầu framework/libraries hỗ trợ asyncio
Như vậy, asyncio giúp lập trình viên Python viết được các ứng dụng bất đồng bộ một cách hiệu quả, tiết kiệm tài nguyên. Nó rất phù hợp với các tasks I/O bound như networking, web services, databases...
Ví dụ
Để gửi nhiều yêu cầu HTTP đồng thời sử dụng asyncio
trong Python, bạn có thể sử dụng thư viện httpx
, một thư viện HTTP client hỗ trợ asyncio và rất tương đồng với requests
. Dưới đây là một ví dụ đơn về cách sử dụng asyncio
và httpx
để gửi nhiều yêu cầu HTTP:
import asyncio
import httpx
async def fetch_data(url):
async with httpx.AsyncClient() as client:
response = await client.get(url)
return {"url": url, "status_code": response.status_code, "content": response.text}
async def main():
urls = ["https://example.com", "https://example.org", "https://example.net"]
tasks = [fetch_data(url) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(result)
if __name__ == "__main__":
asyncio.run(main())
Trong đoạn mã này, hàm fetch_data
được định nghĩa để gửi yêu cầu GET đồng thời sử dụng httpx.AsyncClient()
. Trong hàm main
, một danh sách các URL cần gửi yêu cầu được tạo ra, sau đó một danh sách các fetch_data
task được tạo từ danh sách URL này. Cuối cùng, asyncio.gather
được sử dụng để chờ đợi tất cả các task hoàn thành.
asyncio.Semaphore
để giới hạn số lượng yêu cầu HTTP đồng thời:
import asyncio
import httpx
async def fetch_data(url, semaphore):
async with semaphore:
async with httpx.AsyncClient() as client:
response = await client.get(url)
return {"url": url, "status_code": response.status_code, "content": response.text}
async def main():
urls = ["https://example.com", "https://example.org", "https://example.net"]
# Đặt giới hạn là 2 yêu cầu đồng thời
semaphore = asyncio.Semaphore(2)
tasks = [fetch_data(url, semaphore) for url in urls]
results = await asyncio.gather(*tasks)
for result in results:
print(result)
if __name__ == "__main__":
asyncio.run(main())
Trong đoạn mã này, asyncio.Semaphore(2)
được sử dụng để giới hạn số lượng yêu cầu HTTP đồng thời là 2. Hàm fetch_data
được bao bọc bởi async with semaphore:
để đảm bảo rằng không quá 2 yêu cầu được thực hiện cùng một lúc.
Điều này có thể hữu ích khi bạn muốn kiểm soát đồng thời số lượng yêu cầu để tránh tình trạng quá tải server hoặc để duy trì hiệu suất ổn định trong các tình huống yêu cầu nhiều tài nguyên.
Lưu ý rằng bạn cũng có thể điều chỉnh số lượng yêu cầu đồng thời bằng cách sử dụng asyncio.Semaphore
để giới hạn đồng thời số lượng task đang chạy. Điều này có thể hữu ích khi bạn muốn kiểm soát đồng thời tác vụ HTTP để tránh quá tải server hoặc giữ cho các nguồn tài nguyên được quản lý hiệu quả. Trong FastAPI, thường thì bạn không cần tạo event loop bằng cách sử dụng asyncio.get_event_loop() vì FastAPI tự động quản lý event loop. FastAPI sử dụng Starlette làm backend và Starlette sử dụng asyncio để quản lý các hàm bất đồng bộ.