Hướng dẫn is python asynchronous
Bất đồng bộ là một khái niệm rất hay gặp trong các ngôn ngữ lập trình như Javascript, Kotlin hay Python. Đặc biệt, các lập trình viên làm việc nhiều với networking như các web developer thường xuyên phải làm việc với khái niệm này. Trong bài viết này, mình sẽ giải thích một trong các thành phần cấu tạo nên hệ sinh thái async programming trong Python và tất nhiên, nó cũng mang tư tưởng này lên một số ngôn ngữ khác. Show Nội dung chính
Mục tiêu
Bất đồng bộ là gì?Theo Wikipedia
Hình ảnh bên dưới cho chúng ta thấy một vài giải pháp cho các vấn đề về bất đồng bộ Hệ sinh thái của lập trình bất đồng bộLưu ý, các thread trong Python là các native thread, nhưng do một vài policy (cụ thể là trong bản cpython), GIL (Global Interpreter Lock) sẽ không cho phép chúng chạy 2 thread đồng thời. Do đó, các Python thread không thực xử xử lí song song và thật sự mình không thích sử dụng chúng do:
Chúng ta có thể thấy rằng, các thread và process đều sở hữu một không gian bộ nhớ của riêng nó, do đó, chúng có thể thực hiện các công việc độc lập với main thread hay main process Trái lại, event loop duy trì các task, các task này chia sẻ bộ nhớ chung và chúng ta phải trả
lời câu hỏi: Khi nào chúng ta sử dụng event loop, thread hay process?Trong khoa học máy tính, chúng ta có thể phân task thành 2 loại:
Ví dụ:
Qua ví ví dụ trên, ta thây rằng event loop chỉ thực sự hữu dụng trong vấn đề liên quan đến IO bound. Chúng được sử dụng trong các event system và những hệ thống tương tự. Chúng có thể là giải pháp tốt nhất cho các vấn để liên quan đến IO bound. Các vấn đề trong mô hình event loopChúng ta đã biết rằng chúng ta có một context của một hàm bất kì. Context này bao gồm các biến và chúng được giải phóng sau khi hàm kết thúc (giải phóng ra khỏi stack). Trong I/O-bound task, chúng ta sẽ có một vài lệnh lấy dữ liệu (IO operation) mà tại đó chúng ta cần tối ưu (như ví dụ về David, anh ta có thể tạm ngưng các task bị pending bởi test và chuyển qua làm task khác rồi quay lại làm các task của anh ta). Như vậy các ngắt là một vấn đề, làm sao chúng ta có thể tạo ra các ngắt trong hàm mà vẫn giữ được context của hàm để có thể thực thi tiếp? Tại các ngắt đó, hàm đang thực thi(callee) cần trao lại quyền điều khiển chương trình (program control) cho nơi đã gọi hàm đó (caller), ở đây thực chất là event loop và chúng ta cũng cần bắt đầu tại điểm ngắt này khi caller trao quyền điều khiển cho callee khi nó được thực thi tiếp. ✅ Giải pháp ở đây là sử dụng coroutine Coroutine là gì?Donald Knuth nói rằng:
Đúng vậy, tổng quát hoá, các hàm bình thường chúng ta hay sử dụng (hàm bị giải phóng context sau khi thoát khỏi hàm) là trường hợp đặc biệt của coroutine - nơi đó context có thể được giữ lại khi nó được tạm dùng. Subroutine vs CoroutineTại sao coroutine lại hữu dụng cho event system?
Đơn vị làm việc
Câu hỏi là: Vậy coroutine làm việc như thế nào? Làm thế nào để cài đặt một coroutine?
Về cơ bản, nó cố gắng lưu lại trạng thái của hàm trong biến Trong đoạn code này, điểm chính là biến Và ở bên dưới, nó được chuyển sang Python code từ code C ở trên
Bạn có thể chuyển Python code này sang C?
Sau đó, bạn nên thấy kết quản như thế này
Mình có thể build bất kì một coroutine nào trong C. Bạn có thể làm được điều đó không?
Kết quả có thể thấy
Chúng ta có thể thấy rằng, coroutine cần một không gian bộ nhớ tĩnh để lưu lại trạng thái khi nó suspend và khôi phục lại mà không bị mất context. Trong C, không gian tĩnh là các biến static, chúng duy trì bởi OS khi một hàm thoát. Trong Python, context của hàm được lưu trữ trong các stack frame. Hãy nghĩ về các coroutine như là các đoạn của một chương trình, không có bộ nhớ riêng, không thực thi song song và cực kì an toàn. Coroutine vs ThreadsCoroutine giảm các lỗi do xử lí đa tiến trình (đa luồng) gây ra và mình nghĩ nó là giải pháp tốt nhất cho các task liên quan đến networking bởi nó chỉ tồn tại trong 1 tiến trình. Trong Python, chúng ta có thể định nghĩa coroutine bằng việc sử dụng lệnh
Sau đó, kết quả có thể thấy
Generator là một trường hợp đặc biệt của coroutine, chúng chỉ có thể sinh(produce) dữ liệu mà không thể tiêu thụ(consuming) dữ liệu.
Và đây là kết quả
Chúng ta có thể refactor đoạn code với
Build binary tree with |