OK, đây là của tôi về nó.
Có một thứ gọi là coroutines đã được biết đến trong nhiều thập kỷ. ( "Knuth và Hopper" đẳng cấp "trong nhiều thập kỷ") Họ là những khái quát của chương trình con , trong đó không chỉ làm họ nhận được và kiểm soát phát hành tại chức năng bắt đầu và tuyên bố trở lại, nhưng họ cũng làm điều đó tại các điểm cụ thể ( điểm treo ). Một chương trình con là một coroutine không có điểm treo.
Chúng có thể DỄ DÀNG để thực hiện với các macro C, như được trình bày trong bài báo sau về "protothreads". ( http://dunkels.com/adam/dunkels06protothreads.pdf ) Đọc nó. Tôi sẽ đợi...
Điểm mấu chốt của điều này là các macro tạo ra switch
một case
nhãn lớn và nhãn ở mỗi điểm treo. Tại mỗi điểm treo, hàm sẽ lưu giá trị của case
nhãn ngay sau đó để nó biết nơi tiếp tục thực hiện lần sau khi được gọi. Và nó trả lại quyền kiểm soát cho người gọi.
Điều này được thực hiện mà không sửa đổi luồng kiểm soát rõ ràng của mã được mô tả trong "protothread".
Bây giờ hãy tưởng tượng rằng bạn có một vòng lặp lớn gọi lần lượt tất cả các "protothreads" này và bạn sẽ thực hiện đồng thời "protothreads" trên một chuỗi.
Cách tiếp cận này có hai nhược điểm:
- Bạn không thể giữ trạng thái trong các biến cục bộ giữa các lần thay đổi.
- Bạn không thể tạm dừng "protothread" từ độ sâu cuộc gọi tùy ý. (tất cả các điểm treo phải ở mức 0)
Có cách giải quyết cho cả hai:
- Tất cả các biến cục bộ phải được kéo lên đến bối cảnh của protothread (bối cảnh vốn đã cần thiết bởi thực tế protothread phải lưu trữ điểm nối lại tiếp theo của nó)
- Nếu bạn cảm thấy bạn thực sự cần phải gọi một protothread khác từ một protothread, hãy "sinh ra" một protothread trẻ em và đình chỉ cho đến khi hoàn thành đứa trẻ.
Và nếu bạn có hỗ trợ trình biên dịch để thực hiện công việc viết lại mà các macro và cách khắc phục, bạn có thể viết mã protothread như bạn dự định và chèn các điểm treo với một từ khóa.
Và đây là những gì async
và await
tất cả là về: tạo ra các coroutines (stackless).
Các coroutines trong C # được hợp nhất thành các đối tượng của lớp (chung hoặc không chung) Task
.
Tôi thấy những từ khóa này rất sai lệch. Đọc tinh thần của tôi là:
async
là "đáng ngờ"
await
là "đình chỉ cho đến khi hoàn thành"
Task
là "tương lai"
Hiện nay. Chúng ta có thực sự cần phải đánh dấu chức năng async
? Ngoài việc nói rằng nó sẽ kích hoạt các cơ chế viết lại mã để làm cho hàm trở thành một coroutine, nó giải quyết một số sự mơ hồ. Hãy xem xét mã này.
public Task<object> AmIACoroutine() {
var tcs = new TaskCompletionSource<object>();
return tcs.Task;
}
Giả sử đó async
là không bắt buộc, đây là một coroutine hoặc một chức năng bình thường? Trình biên dịch có nên viết lại nó như một coroutine hay không? Cả hai có thể có thể với ngữ nghĩa cuối cùng khác nhau.