Các nhà phát triển Python tranh luận về Virtual Threads như một giải pháp thay thế cho sự phức tạp của Async/Await

Nhóm Cộng đồng BigGo
Các nhà phát triển Python tranh luận về Virtual Threads như một giải pháp thay thế cho sự phức tạp của Async/Await

Cộng đồng Python đang tích cực thảo luận về một sự chuyển đổi tiềm năng từ các mẫu async/await sang virtual threads, được thúc đẩy bởi sự thất vọng ngày càng tăng với độ phức tạp của các mô hình đồng thời hiện tại. Cuộc tranh luận này làm nổi bật những câu hỏi cơ bản về cách Python nên xử lý lập trình đồng thời trong tương lai.

Vấn đề Function Coloring

Một trong những vấn đề quan trọng nhất thúc đẩy cuộc thảo luận này là điều mà các nhà phát triển gọi là function coloring - yêu cầu đánh dấu các hàm là async và sử dụng từ khóa await trong toàn bộ codebase. Điều này tạo ra sự chia tách giữa mã đồng bộ và không đồng bộ mà không thể dễ dàng tương tác với nhau. Nhiều nhà phát triển so sánh điều này với aspect-oriented programming từ những năm 1990, cho rằng nó tăng thêm độ phức tạp của compiler mà không mang lại lợi ích tương xứng.

Cộng đồng đã lưu ý rằng async/await đã giúp nhiều người tiếp xúc với các khái niệm lập trình đồng thời, nhưng với cái giá phải trả là yêu cầu máy móc nội bộ phức tạp rò rỉ vào mã người dùng. Sự phức tạp này trở nên còn có vấn đề hơn với những thay đổi free-threading gần đây của Python, điều này đưa ra các mối quan tâm về threading cùng với độ phức tạp của async.

Thuật ngữ kỹ thuật chính

  • Function Coloring: Yêu cầu phải đánh dấu các hàm là async/await, tạo ra các đường dẫn mã đồng bộ/bất đồng bộ không tương thích
  • Structured Concurrency: Mô hình lập trình trong đó các tác vụ con không thể tồn tại lâu hơn các tác vụ cha của chúng
  • Virtual Threads: Các luồng nhẹ được quản lý bởi runtime thay vì hệ điều hành
  • Stackful vs Stackless Coroutines: Các cách tiếp cận khác nhau để lưu trạng thái thực thi trong quá trình chuyển đổi tác vụ
  • Free-threading: Tính năng gần đây của Python cho phép thực thi song song thực sự mà không cần Global Interpreter Lock (GIL)

Virtual Threads như một giải pháp thay thế đơn giản hơn

Virtual threads, tương tự như goroutines của Go hoặc Project Loom của Java, cung cấp một cách tiếp cận khác. Thay vì yêu cầu cú pháp đặc biệt, chúng sẽ cho phép các lời gọi hàm blocking thông thường trong khi runtime xử lý việc lập lịch đằng sau hậu trường. Điều này có nghĩa là các nhà phát triển có thể viết mã đơn giản, trông tuần tự nhưng thực sự chạy đồng thời.

Mô hình được đề xuất sẽ sử dụng structured concurrency thông qua các nhóm thread, đảm bảo rằng các child threads không tồn tại lâu hơn parent của chúng. Mối quan hệ này làm cho luồng thông tin rõ ràng hơn so với các biến thread-local truyền thống. Tuy nhiên, việc triển khai điều này trong Python gặp phải những thách thức do các quy tắc scoping của ngôn ngữ và thiếu khai báo biến.

Các cân nhắc về hiệu suất và bộ nhớ

Những người chỉ trích chỉ ra nghiên cứu cho thấy rằng stackless coroutines (mô hình async/await hiện tại) hiệu quả về mặt bộ nhớ hơn và có chi phí chuyển đổi thấp hơn so với các lựa chọn thay thế stackful như virtual threads. Tuy nhiên, các thành viên cộng đồng lập luận rằng những khác biệt về hiệu suất này là không đáng kể trong bối cảnh của Python, nơi mà overhead của interpreter chiếm ưu thế.

Thực sự không có lý do gì để python lại chọn mô hình async/await.

Cuộc tranh luận mở rộng ra ngoài hiệu suất thuần túy đến năng suất của nhà phát triển. Một số người lập luận rằng các giải pháp đơn giản hơn, không tối ưu có thể nhanh hơn trong tổng thời gian phát triển, ngay cả khi chúng chậm hơn khi chạy.

So sánh Mô hình Đồng thời

Phương pháp Độ phức tạp Cú pháp Sử dụng Bộ nhớ Trải nghiệm Nhà phát triển Hiệu suất
Async/Await Cao (function coloring) Thấp Phức tạp Tối ưu
Virtual Threads Thấp (hàm thông thường) Trung bình Đơn giản Đủ tốt
Gevent Thấp (monkey patching) Trung bình Đơn giản Tốt
Traditional Threads Thấp Cao Phức tạp (locks) Không ổn định

Thách thức thực tế với các thư viện

Cuộc thảo luận tiết lộ các vấn đề thực tế với các thư viện async hiện tại. Các vấn đề với xử lý cancellation trong các thư viện phổ biến như những thư viện xử lý file I/O có thể gây ra deadlocks khi được sử dụng với các mẫu structured concurrency. Những vấn đề này xuất phát từ việc trộn lẫn mã async với thread pools, tạo ra các tình huống mà việc cancellation thích hợp trở nên không thể.

Các cách tiếp cận thay thế đang thu hút sự chú ý

Một số nhà phát triển đã đề cập đến những trải nghiệm thành công với gevent, sử dụng monkey patching để cung cấp cooperative multitasking mà không có function coloring. Những người khác chỉ ra các ngôn ngữ như Elixir như những ví dụ về các mô hình đồng thời đơn giản hơn tránh hoàn toàn những phức tạp này.

Cách tiếp cận của hệ sinh thái Rust cũng được nhắc đến, nơi các nhà phát triển có thể lựa chọn giữa các mô hình đồng thời khác nhau thay vì bị khóa vào một cách tiếp cận. Sự linh hoạt này cho phép các dự án lựa chọn công cụ tốt nhất cho nhu cầu cụ thể của họ.

Nhìn về tương lai

Mặc dù virtual threads không được lên kế hoạch chính thức cho Python, cuộc thảo luận phản ánh sự bất mãn rộng rãi hơn với các mẫu async hiện tại. Cộng đồng dường như chia rẽ giữa những người muốn thúc đẩy các cải tiến async/await và những người ủng hộ một sự chuyển đổi cơ bản sang đồng thời dựa trên thread.

Cuộc tranh luận cuối cùng tập trung vào việc liệu Python nên ưu tiên các tối ưu hóa hiệu suất lý thuyết hay trải nghiệm của nhà phát triển và sự đơn giản của mã. Khi ngôn ngữ phát triển với các tính năng như free-threading, những quyết định kiến trúc này trở nên ngày càng quan trọng đối với hướng đi tương lai của Python.

Tham khảo: From Async/Await to Virtual Threads