Python Async, vì sao vẫn chưa trở thành xu hướng chủ đạo?
(tonybaloney.github.io)Python Async, vì sao vẫn chưa trở thành xu hướng chủ đạo?
asyncio của Python là một công cụ mạnh mẽ có thể giảm thời gian chờ và tăng đáng kể hiệu quả của chương trình trong các môi trường có nhiều tác vụ I/O (vào/ra). Tuy nhiên, dù có nhiều ưu điểm, không phải mọi lập trình viên Python đều tích cực sử dụng nó, và có một vài lý do mang tính nền tảng đằng sau điều này.
1. Đầu óc trở nên rối hơn: gánh nặng nhận thức
Rào cản lớn nhất là độ phức tạp. Mã đồng bộ có tính trực quan vì ta chỉ cần lần theo luồng thực thi tuần tự từ trên xuống dưới như khi đọc một cuốn sách.
Nhưng mã bất đồng bộ thì khác. Nó giống như một đầu bếp, trong lúc luộc mì để làm pasta (thời gian chờ), đồng thời tranh thủ làm nước sốt, và khi còn thời gian thì sơ chế rau củ. Kết quả là món ăn có thể hoàn thành nhanh hơn, nhưng trong đầu người đầu bếp luôn phải để ý nhiều trạng thái công việc như “mì chín đến đâu rồi?”, “nước sốt có bị cháy không?”.
Vì luồng thực thi của mã liên tục chuyển từ chỗ này sang chỗ khác, nên rất khó nắm bắt tác vụ nào đang chạy và tác vụ nào đang chờ. Điều này khiến quá trình gỡ lỗi, đặc biệt là khi cần truy vết nguyên nhân gây bug, trở nên rất khó khăn.
2. Hệ sinh thái tách rời: khả năng tương thích thư viện
Một vấn đề khác của Python là hệ sinh thái thư viện bị chia thành “đồng bộ” và “bất đồng bộ”. Rất nhiều thư viện nổi tiếng và tiện dụng (ví dụ: thư viện HTTP chuẩn requests hay nhiều ORM) chỉ hoạt động theo cách đồng bộ.
Nếu vô tình dùng thư viện đồng bộ trong mã bất đồng bộ, thì trong thời gian đoạn mã đồng bộ đó chạy, “event loop” — lợi thế lớn nhất của bất đồng bộ — sẽ bị chặn hoàn toàn. Điều này giống như mở nhiều làn đường nhưng chỉ sử dụng đúng một làn, khiến việc dùng bất đồng bộ mất đi ý nghĩa. Để giải quyết vấn đề này, bạn phải học và áp dụng các thư viện riêng có hỗ trợ bất đồng bộ (aiohttp, asyncpg, v.v.), và điều đó càng làm đường cong học tập trở nên dốc hơn.
3. Mỗi lần chỉ xử được một việc: GIL (khóa thông dịch toàn cục)
GIL (Global Interpreter Lock) là một trong những đặc điểm cố hữu của Python, cơ chế giới hạn để trong một tiến trình, dù có nhiều luồng thì tại cùng một thời điểm cũng chỉ một luồng duy nhất được thực thi. asyncio hoạt động trên một luồng đơn nên không xung đột trực tiếp với GIL, nhưng sự tồn tại của GIL vẫn giới hạn phạm vi ứng dụng của async.
asyncio được tối ưu để tận dụng thời gian chờ I/O (chờ phản hồi mạng, chờ đọc tệp, v.v.). Nhưng nếu bên trong hàm async có một tác vụ nặng về CPU với tính toán rất phức tạp, thì toàn bộ event loop sẽ bị dừng cho đến khi phép tính đó hoàn tất. Trong khoảng thời gian đó, các tác vụ I/O khác không thể làm gì ngoài việc chờ. Cuối cùng, với những công việc thực sự cần xử lý song song, vẫn phải dùng các kỹ thuật khác như multiprocessing.
4. Hy vọng cho tương lai: Python 3.14 và việc loại bỏ GIL
Tuy nhiên, có một tín hiệu rất đáng hy vọng đối với những giới hạn này. Đó là xu hướng loại bỏ GIL theo cách tùy chọn, được giới thiệu ở mức thử nghiệm từ Python 3.13 và được kỳ vọng sẽ trưởng thành hơn trong phiên bản 3.14.
Sự thay đổi này, được thúc đẩy thông qua đề xuất PEP 703, hướng tới mục tiêu cho phép lập trình viên chạy mã Python mà không cần GIL nếu họ muốn. Nếu điều này trở thành hiện thực, Python sẽ có thể đạt được đa luồng thực sự, nơi nhiều luồng có thể đồng thời tận dụng nhiều lõi CPU.
Khi kết hợp với asyncio, điều này có thể tạo ra sức mạnh cộng hưởng rất lớn. Tác vụ I/O có thể được xử lý hiệu quả bằng asyncio, còn các tác vụ CPU nặng tính toán có thể được chuyển sang các luồng riêng để xử lý song song mà không còn bị ràng buộc bởi GIL. Sự thay đổi này được kỳ vọng sẽ trở thành bước ngoặt lớn cho hệ sinh thái Python và phá bỏ nhiều rào cản từng cản trở việc chấp nhận lập trình async.
Chưa có bình luận nào.