Trên thực tế, async / await không phải là điều kỳ diệu. Toàn bộ chủ đề khá rộng nhưng để có câu trả lời đủ nhanh nhưng đầy đủ cho câu hỏi của bạn, tôi nghĩ chúng ta có thể quản lý.
Hãy giải quyết một sự kiện nhấn nút đơn giản trong ứng dụng Windows Forms:
public async void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before awaiting");
await GetSomethingAsync();
Console.WriteLine("after awaiting");
}
Tôi rõ ràng sẽ không nói về bất cứ điều gìGetSomethingAsync
sẽ trở lại bây giờ. Chúng ta hãy nói rằng đây là thứ sẽ hoàn thành sau 2 giây.
Trong một thế giới truyền thống, không đồng bộ, trình xử lý sự kiện nhấn nút của bạn sẽ trông giống như thế này:
public void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("before waiting");
DoSomethingThatTakes2Seconds();
Console.WriteLine("after waiting");
}
Khi bạn nhấp vào nút trong biểu mẫu, ứng dụng sẽ xuất hiện đóng băng trong khoảng 2 giây, trong khi chúng tôi chờ phương pháp này hoàn tất. Điều gì xảy ra là "bơm thông điệp", về cơ bản là một vòng lặp, bị chặn.
Vòng lặp này liên tục hỏi các cửa sổ "Có ai đã làm gì đó chưa, như di chuyển chuột, nhấp vào cái gì đó? Tôi có cần sơn lại cái gì không? Nếu vậy, hãy nói cho tôi biết!" và sau đó xử lý "cái gì đó". Vòng lặp này có một thông báo mà người dùng đã nhấp vào "button1" (hoặc loại tin nhắn tương đương từ Windows) và cuối cùng đã gọi button1_Click
phương thức của chúng tôi ở trên. Cho đến khi phương thức này trở lại, vòng lặp này hiện đang bị kẹt chờ đợi. Điều này mất 2 giây và trong thời gian này, không có tin nhắn nào đang được xử lý.
Hầu hết mọi thứ liên quan đến windows đều được thực hiện bằng cách sử dụng tin nhắn, điều đó có nghĩa là nếu vòng lặp tin nhắn ngừng bơm tin nhắn, thậm chí chỉ trong một giây, nó sẽ nhanh chóng được người dùng chú ý. Chẳng hạn, nếu bạn di chuyển notepad hoặc bất kỳ chương trình nào khác lên trên chương trình của chính bạn, rồi lại đi, một loạt các thông điệp sơn được gửi đến chương trình của bạn cho biết khu vực nào của cửa sổ đột nhiên xuất hiện trở lại. Nếu vòng lặp thông báo xử lý các tin nhắn này đang chờ một cái gì đó, bị chặn, thì không có bức tranh nào được thực hiện.
Vì vậy, nếu trong ví dụ đầu tiên, async/await
không tạo chủ đề mới, làm thế nào để làm điều đó?
Vâng, những gì xảy ra là phương pháp của bạn được chia thành hai. Đây là một trong những loại chủ đề rộng lớn vì vậy tôi sẽ không đi sâu vào chi tiết nhưng đủ để nói phương pháp này được chia thành hai điều sau:
- Tất cả các mã dẫn đến
await
, bao gồm cả lệnh gọi đếnGetSomethingAsync
- Tất cả các mã sau đây
await
Hình minh họa:
code... code... code... await X(); ... code... code... code...
Sắp xếp lại:
code... code... code... var x = X(); await X; code... code... code...
^ ^ ^ ^
+---- portion 1 -------------------+ +---- portion 2 ------+
Về cơ bản phương thức thực hiện như thế này:
- Nó thực thi mọi thứ lên đến
await
Nó gọi GetSomethingAsync
phương thức, thực hiện công việc của nó và trả về một cái gì đó sẽ hoàn thành trong 2 giây trong tương lai
Cho đến nay chúng ta vẫn ở trong cuộc gọi ban đầu đến button1_Click, xảy ra trên luồng chính, được gọi từ vòng lặp tin nhắn. Nếu mã dẫn đến await
mất nhiều thời gian, giao diện người dùng sẽ vẫn đóng băng. Trong ví dụ của chúng tôi, không quá nhiều
Những gì await
từ khóa, cùng với một số phép thuật trình biên dịch thông minh, về cơ bản là nó giống như "Ok, bạn biết gì không, tôi sẽ chỉ đơn giản trở về từ trình xử lý sự kiện nhấn nút ở đây. Khi bạn (như, điều chúng ta ' Đang chờ) hãy đi xung quanh để hoàn thành, hãy cho tôi biết vì tôi vẫn còn một số mã để thực thi ".
Trên thực tế, nó sẽ cho lớp SyncizationContext biết rằng nó đã được thực hiện, tùy thuộc vào bối cảnh đồng bộ hóa thực tế đang diễn ra ngay bây giờ, sẽ xếp hàng để thực thi. Lớp ngữ cảnh được sử dụng trong chương trình Windows Forms sẽ xếp hàng nó bằng cách sử dụng hàng đợi mà vòng lặp thông báo đang bơm.
Vì vậy, nó trở lại vòng lặp tin nhắn, giờ đây miễn phí để tiếp tục bơm tin nhắn, như di chuyển cửa sổ, thay đổi kích thước hoặc nhấp vào các nút khác.
Đối với người dùng, giao diện người dùng hiện đang phản hồi lại, xử lý các lần nhấp nút khác, thay đổi kích thước và quan trọng nhất là vẽ lại , do đó, nó dường như không bị đóng băng.
- 2 giây sau, điều chúng tôi đang chờ hoàn tất và điều xảy ra bây giờ là nó (tốt, bối cảnh đồng bộ hóa) đặt một tin nhắn vào hàng đợi mà vòng lặp tin nhắn đang xem, nói rằng "Này, tôi có thêm một số mã cho bạn thực thi "và mã này là tất cả mã sau khi chờ đợi.
- Khi vòng lặp thông báo đến được thông báo đó, về cơ bản nó sẽ "nhập lại" phương thức đó khi nó dừng lại, ngay sau đó
await
và tiếp tục thực hiện phần còn lại của phương thức. Lưu ý rằng mã này được gọi lại từ vòng lặp thông báo, vì vậy nếu mã này xảy ra để làm gì đó dài mà không sử dụng async/await
đúng cách, nó sẽ lại chặn vòng lặp thông báo
Có nhiều bộ phận chuyển động dưới mui xe ở đây vì vậy đây là một số liên kết đến nhiều thông tin hơn, tôi sẽ nói "nếu bạn cần nó", nhưng chủ đề này khá rộng và khá quan trọng để biết một số bộ phận chuyển động đó . Lúc nào bạn cũng sẽ hiểu rằng async / await vẫn là một khái niệm rò rỉ. Một số hạn chế và vấn đề cơ bản vẫn rò rỉ vào mã xung quanh và nếu không, bạn thường phải gỡ lỗi một ứng dụng bị hỏng ngẫu nhiên mà dường như không có lý do chính đáng.
OK, vậy điều gì sẽ xảy ra nếu GetSomethingAsync
quay lên một chuỗi sẽ hoàn thành sau 2 giây? Vâng, sau đó rõ ràng là có một chủ đề mới trong chơi. Tuy nhiên, luồng này không phải vì tính không đồng bộ của phương thức này, mà là do người lập trình của phương thức này đã chọn một luồng để triển khai mã không đồng bộ. Hầu như tất cả các I / O không đồng bộ không sử dụng một luồng, chúng sử dụng những thứ khác nhau. async/await
tự chúng không quay các luồng mới nhưng rõ ràng "những điều chúng ta chờ đợi" có thể được thực hiện bằng các luồng.
Có nhiều thứ trong .NET không nhất thiết phải tự tạo một luồng nhưng vẫn không đồng bộ:
- Yêu cầu web (và nhiều thứ khác liên quan đến mạng cần có thời gian)
- Đọc và ghi tệp không đồng bộ
- và nhiều hơn nữa, một dấu hiệu tốt là nếu lớp / giao diện trong câu hỏi đã được đặt tên phương pháp
SomethingSomethingAsync
hay BeginSomething
và EndSomething
và có một IAsyncResult
tham gia.
Thông thường những thứ này không sử dụng một chủ đề dưới mui xe.
OK, vì vậy bạn muốn một số "công cụ chủ đề rộng"?
Chà, hãy hỏi Thử Roslyn về nút bấm của chúng tôi:
Hãy thử Roslyn
Tôi sẽ không liên kết trong lớp được tạo đầy đủ ở đây nhưng đó là thứ khá đẫm máu.