Asyncio.gather: Chơi Game Đa Nhiệm, Chill Phết Với Python!
Python

Asyncio.gather: Chơi Game Đa Nhiệm, Chill Phết Với Python!

Author

Admin System

@root

Ngày xuất bản

21 Mar, 2026

Lượt xem

4 Lượt

"asyncio_gather"

Chào các homie, anh Creyt đây! Hôm nay, chúng ta sẽ 'bóc tách' một 'skill' cực đỉnh trong Python giúp các em 'chill' hơn với mấy cái task 'tốn thời gian' – đó chính là asyncio.gather.

asyncio.gather: 'Ông Bầu' Của Các Task Đa Nhiệm

Các em hình dung thế này: Cuộc sống Gen Z của chúng ta lúc nào cũng 'multi-tasking' đúng không? Vừa lướt TikTok, vừa chat Zalo, vừa nghe nhạc, vừa làm bài tập. Trong lập trình cũng vậy, đôi khi chúng ta cần thực hiện nhiều công việc cùng lúc mà không phải chờ đợi từng cái một xong xuôi mới làm cái tiếp theo. Ví dụ, em cần tải 5 bức ảnh từ Instagram, gọi 3 API khác nhau để lấy dữ liệu, hay xử lý 7 file trong một thư mục.

Nếu làm tuần tự (synchronous), em sẽ phải đợi ảnh 1 tải xong mới đến ảnh 2, rồi ảnh 3... Cứ thế thì 'mòn mỏi' lắm, 'đợi chờ là hạnh phúc' nhưng mà 'hạnh phúc' này hơi bị 'lâu'.

asyncio.gather chính là 'cứu tinh' ở đây. Nó giống như một 'ông bầu' tài ba, nhận tất cả các 'nghệ sĩ' (các coroutine/task độc lập) của em và 'cử' họ đi biểu diễn cùng lúc. 'Ông bầu' sẽ đứng đó, chờ cho đến khi tất cả các 'nghệ sĩ' ấy biểu diễn xong xuôi và 'gom' hết các kết quả về cho em trong một 'rổ' duy nhất. Em không cần phải lo thằng nào xong trước, thằng nào xong sau, miễn là kết quả của tất cả đều được thu thập một cách 'tử tế'.

Nói cách khác, asyncio.gather cho phép em chạy nhiều coroutine đồng thời (concurrently) và trả về một list chứa kết quả của tất cả các coroutine đó, theo đúng thứ tự mà em đã truyền vào.

Code Ví Dụ: 'Đa Nhiệm' Ngay Và Luôn!

Để minh họa cho cái sự 'chill' này, anh Creyt sẽ cho em một ví dụ 'kinh điển' về việc tải dữ liệu từ các 'server ảo'.

import asyncio
import time

# Một coroutine mô phỏng việc tải dữ liệu từ một server nào đó
async def download_data(server_id, delay):
    print(f"[{server_id}] Bắt đầu tải dữ liệu... Đợi {delay} giây.")
    await asyncio.sleep(delay)  # Mô phỏng thời gian chờ I/O
    print(f"[{server_id}] Tải xong dữ liệu.")
    return f"Dữ liệu từ server {server_id} đã sẵn sàng!"

async def main():
    start_time = time.time()

    print("\n--- Chạy tuần tự (Synchronous-like) ---")
    # Nếu chạy tuần tự, mỗi task sẽ đợi task trước hoàn thành
    result1 = await download_data("Server A", 3)
    result2 = await download_data("Server B", 2)
    result3 = await download_data("Server C", 1)
    print(f"Kết quả tuần tự: {result1}, {result2}, {result3}")
    print(f"Tổng thời gian chạy tuần tự: {time.time() - start_time:.2f} giây")

    start_time_gather = time.time()
    print("\n--- Chạy với asyncio.gather (Concurrent) ---")
    # Sử dụng asyncio.gather để chạy các coroutine đồng thời
    results = await asyncio.gather(
        download_data("Server X", 3),
        download_data("Server Y", 2),
        download_data("Server Z", 1)
    )
    print(f"Kết quả với gather: {results}")
    print(f"Tổng thời gian chạy với gather: {time.time() - start_time_gather:.2f} giây")

if __name__ == "__main__":
    asyncio.run(main())

Giải thích:

Trong ví dụ trên, khi chạy tuần tự, tổng thời gian sẽ là 3 + 2 + 1 = 6 giây. Nhưng khi dùng asyncio.gather, các task download_data cho Server X, Y, Z sẽ được 'khởi chạy' gần như cùng lúc. Python sẽ 'nhảy' giữa các task trong lúc chúng đang 'ngủ' (do await asyncio.sleep), tận dụng hiệu quả thời gian chờ. Do đó, tổng thời gian chạy với gather sẽ chỉ bằng thời gian của task lâu nhất (ở đây là 3 giây của Server X), cộng thêm một chút overhead nhỏ. 'Chill' không?

Illustration

Mẹo Vặt Từ Anh Creyt (Best Practices)

  1. Chỉ 'gom' task độc lập: gather 'phát huy' sức mạnh tối đa khi các task em 'gom' vào không phụ thuộc vào kết quả của nhau. Nếu task B cần kết quả của task A, thì gather không phải là 'skill' phù hợp. Lúc đó, em cần await task A trước rồi mới await task B.
  2. Xử lý 'drama' (Exceptions): Đôi khi, một trong các task của em có thể 'gặp sự cố' (raise an exception). Mặc định, gather sẽ 'dừng cuộc chơi' và 'ném' exception đó ra ngoài ngay lập tức. Nhưng nếu em muốn các task khác vẫn tiếp tục chạy và thu thập tất cả các exception, hãy dùng return_exceptions=True:
    results = await asyncio.gather(
        download_data("Server Error", 2),
        download_data("Server OK", 1),
        return_exceptions=True # Thêm cái này vào
    )
    # Kết quả sẽ là list chứa cả giá trị trả về và các đối tượng Exception
    # Em có thể kiểm tra từng phần tử trong results để biết task nào lỗi
    
  3. ConcurrencyParallelism: Nhớ nhé, asyncio là về concurrency (đồng thời), không phải parallelism (song song thật sự). Tức là, Python vẫn chạy trên một luồng (thread) duy nhất, nhưng nó rất 'khôn' trong việc chuyển đổi ngữ cảnh giữa các task I/O-bound (như đọc file, gọi API, tải dữ liệu) để không phải chờ đợi 'vô ích'. Nếu em muốn chạy thật sự song song trên nhiều CPU core, em sẽ cần đến multiprocessing.

Ứng Dụng Thực Tế: 'Đời Sống' Của asyncio.gather

asyncio.gather được ứng dụng 'nhan nhản' trong các hệ thống cần tốc độ và hiệu quả:

  • Web Scraper/Crawler: Tải hàng loạt trang web, dữ liệu từ nhiều nguồn khác nhau cùng lúc mà không bị 'delay'.
  • API Gateway/Microservices: Gọi nhiều API từ các dịch vụ nhỏ (microservices) khác nhau để tổng hợp dữ liệu cho một yêu cầu duy nhất của người dùng. Ví dụ, khi em vào một trang thương mại điện tử, để hiển thị thông tin sản phẩm, website có thể gọi API lấy giá, API lấy đánh giá, API lấy thông tin kho hàng... tất cả cùng lúc.
  • Backend cho Mobile/Web Apps: Xử lý hàng trăm, hàng nghìn yêu cầu từ người dùng đồng thời, giúp ứng dụng của em luôn 'mượt mà' và 'phản hồi nhanh'.
  • Data Pipelines: Đọc dữ liệu từ nhiều nguồn, xử lý sơ bộ, và ghi vào nhiều đích khác nhau một cách 'song song' (về mặt I/O).

Thử Nghiệm Và Nên Dùng Cho Case Nào?

Anh Creyt khuyên em nên 'vọc vạch' asyncio.gather khi:

  • Tasks 'ngốn' thời gian chờ I/O: Như đã nói, nếu task của em chủ yếu là chờ đợi (network requests, database queries, file I/O), gather là 'chân ái'.
  • Cần kết quả của nhiều task cùng lúc: Khi em cần tất cả các kết quả từ một tập hợp các công việc độc lập để xử lý tiếp, gather sẽ 'gom' chúng lại gọn gàng.
  • Tối ưu hiệu suất ứng dụng: Muốn ứng dụng của em 'phản hồi' nhanh hơn, 'nuột' hơn, đặc biệt là với các ứng dụng web, API.

Không nên dùng khi các task có mối quan hệ phụ thuộc chặt chẽ, task sau cần kết quả của task trước. Lúc đó, cứ await từng task một là 'chuẩn bài' nhất.

Vậy đó, asyncio.gather không chỉ là một hàm, nó là một 'mindset' giúp em tối ưu hóa thời gian và 'phá đảo' mọi giới hạn về hiệu suất trong các ứng dụng Python. 'Chill' chưa? Hãy 'thực hành' ngay để 'nâng trình' nhé!

Thuộc Series: Python

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!