2.32. Coroutine

Một coroutine là một hàm có thể tạm dừng giữa chừng và sau đó tiếp tục từ nơi nó dừng lại, với tất cả các biến cục bộ vẫn còn nguyên. Nó phản ánh mẫu generator -- một thân hàm có thể bị tạm dừng và tiếp tục -- với một thay đổi về ai điều khiển mỗi lần tiếp tục.

Cú pháp của Python để viết một coroutine là cặp từ khóa async / await. async đánh dấu hàm là coroutine; await đánh dấu các điểm bên trong hàm nơi được phép tạm dừng.

2.32.1. Định nghĩa một coroutine

Một hàm được định nghĩa với async def là một hàm coroutine. Gọi nó sẽ không chạy phần thân; nó trả về một đối tượng coroutine chưa bắt đầu:

async def greet(name):
    print("hello,", name)

coro = greet("Alice")        # body NOT run yet

Đối tượng coroutine đang tạm dừng ngay tại phần đầu của hàm. Cần có thứ gì đó để điều khiển nó nhằm chạy phần thân -- thứ đó là một vòng lặp sự kiện, một thành phần runtime. MicroPython cung cấp một vòng lặp sự kiện trong module asyncio. Hiện tại, hãy xem coroutine như "sẵn sàng chạy, đang chờ một driver".

2.32.2. Tạm dừng bên trong một coroutine

Bên trong một coroutine, biểu thức await tạm dừng thực thi cho đến khi giá trị được chờ sẵn sàng:

async def fetch_and_log():
    data = await read_sensor()
    print("got:", data)

Khi phần thân đến await read_sensor(), coroutine chuyển quyền điều khiển lại cho thứ gì đang chạy nó. Khi read_sensor() hoàn thành, driver tiếp tục coroutine tại dòng tiếp theo, với kết quả được gán cho data.

await chỉ hợp lệ bên trong một coroutine. Sử dụng nó trong một hàm thông thường sẽ gây ra lỗi cú pháp.

2.32.3. Mối quan hệ với generator

Coroutine và generator chia sẻ cùng một cơ chế nền tảng. Sự khác biệt là ai kéo mỗi lần tiếp tục:

  • Một generator yield các giá trị; consumer kéo phần tiếp theo bằng next() hoặc bằng cách lặp.

  • Một coroutine yield quyền điều khiển; vòng lặp sự kiện lên lịch tiếp tục khi thao tác được chờ đã sẵn sàng.

Nếu cơ chế bắt tay yield của generator có ý nghĩa, thì cơ chế bắt tay của coroutine là cùng ý tưởng -- chỉ được điều khiển bởi vòng lặp sự kiện thay vì vòng lặp for.

Một vòng lặp sự kiện là một dispatcher nhỏ duy trì danh sách các coroutine đang chờ điều gì đó (bộ định thời, sự kiện mạng, coroutine khác hoàn thành). Mỗi lần lặp, nó chọn một coroutine đã được thỏa mãn điều kiện chờ, tiếp tục nó cho đến await tiếp theo, rồi ghi lại những gì coroutine đó đang chờ bây giờ và chuyển sang coroutine sẵn sàng khác. Kết quả là nhiều tác vụ tiến triển đồng thời trên một luồng -- mỗi coroutine tự nguyện nhường quyền điều khiển tại các điểm await của nó, và vòng lặp lấp đầy những khoảng thời gian đó bằng bất kỳ coroutine nào khác sẵn sàng tiến triển.

Ở bên dưới, awaityield sử dụng cùng một tính năng runtime Python để tạm dừng và tiếp tục một hàm. Các từ khóa khác nhau vì quy ước xung quanh chúng khác nhau: yield chuyển một giá trị lại cho consumer kéo bằng next(); await chuyển quyền điều khiển cho vòng lặp sự kiện lên lịch tiếp tục khi thao tác được chờ đã sẵn sàng. async / await về cơ bản là cú pháp mới hơn cho mẫu coroutine -- các thư viện cũ hơn xây dựng coroutine trực tiếp trên nền tảng cơ chế generator, sử dụng yield from (được giới thiệu trên Iterator và generator) để ủy quyền tạm dừng giữa các coroutine.

2.32.4. Coroutine cần một driver

Một coroutine không hoạt động nếu không có runtime để điều khiển nó. Định nghĩa một coroutine là ổn; chạy một coroutine cần một vòng lặp sự kiện. Module asyncio của MicroPython cung cấp vòng lặp sự kiện đó. Phần Asyncio hướng dẫn cách khởi động vòng lặp, lên lịch coroutine trên đó, chia sẻ trạng thái giữa chúng bằng lock và event, xử lý hủy bỏ và timeout, và định hình một ứng dụng thực tế xung quanh các từ khóa async / await được giới thiệu ở đây.