Tôi nghĩ rằng bạn đang nhận được một vài điều nhầm lẫn ở đây. Những gì bạn đang yêu cầu đã có thể sử dụng System.Threading.Tasks
, async
và await
trong C # 5 sẽ cung cấp một chút đường cú pháp đẹp hơn cho cùng một tính năng.
Hãy sử dụng ví dụ Winforms - thả nút và hộp văn bản vào biểu mẫu và sử dụng mã này:
private void button1_Click(object sender, EventArgs e)
{
Task.Factory.StartNew<int>(() => DelayedAdd(5, 10))
.ContinueWith(t => DelayedAdd(t.Result, 20))
.ContinueWith(t => DelayedAdd(t.Result, 30))
.ContinueWith(t => DelayedAdd(t.Result, 50))
.ContinueWith(t => textBox1.Text = t.Result.ToString(),
TaskScheduler.FromCurrentSynchronizationContext());
}
private int DelayedAdd(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Chạy nó và bạn sẽ thấy rằng (a) nó không chặn luồng UI và (b) bạn không gặp phải lỗi "hoạt động xuyên chuỗi không hợp lệ" thông thường - trừ khi bạn xóa TaskScheduler
đối số từ cuối cùng ContinueWith
, trong trường hợp nào bạn sẽ
Đây là phong cách tiếp tục không có tiêu chuẩn . Phép thuật xảy ra trong TaskScheduler
lớp và đặc biệt là trường hợp được lấy bởi FromCurrentSynchronizationContext
. Truyền phần này vào bất kỳ phần tiếp theo nào và bạn nói với nó rằng phần tiếp theo phải chạy trên bất kỳ luồng nào được gọi là FromCurrentSynchronizationContext
phương thức - trong trường hợp này là luồng UI.
Người chờ đợi tinh vi hơn một chút theo nghĩa là họ nhận thức được họ đã bắt đầu từ chủ đề nào và chủ đề nào cần tiếp tục diễn ra. Vì vậy, đoạn mã trên có thể được viết tự nhiên hơn một chút :
private async void button1_Click(object sender, EventArgs e)
{
int a = await DelayedAddAsync(5, 10);
int b = await DelayedAddAsync(a, 20);
int c = await DelayedAddAsync(b, 30);
int d = await DelayedAddAsync(c, 50);
textBox1.Text = d.ToString();
}
private async Task<int> DelayedAddAsync(int a, int b)
{
Thread.Sleep(500);
return a + b;
}
Hai nên nhìn rất giống nhau, và trong thực tế họ là rất giống nhau. Các DelayedAddAsync
phương pháp hiện nay trả về một Task<int>
thay vì một int
, và do đó await
chỉ là tát continuations vào mỗi một trong những. Sự khác biệt chính là nó đi dọc theo bối cảnh đồng bộ hóa trên mỗi dòng, vì vậy bạn không phải thực hiện nó một cách rõ ràng như trong ví dụ trước.
Về lý thuyết, sự khác biệt có ý nghĩa hơn rất nhiều. Trong ví dụ thứ hai, mọi dòng đơn trong button1_Click
phương thức thực sự được thực thi trong luồng UI, nhưng chính tác vụ ( DelayedAddAsync
) chạy trong nền. Trong ví dụ đầu tiên, mọi thứ chạy trong nền , ngoại trừ việc gán textBox1.Text
mà chúng ta đã gắn liền với bối cảnh đồng bộ hóa của giao diện người dùng.
Đó là điều thực sự thú vị await
- thực tế là một người phục vụ có thể nhảy vào và ra khỏi cùng một phương thức mà không có bất kỳ cuộc gọi chặn nào. Bạn gọi await
, luồng hiện tại quay lại xử lý tin nhắn và khi hoàn thành, người phục vụ sẽ chọn chính xác nơi nó rời đi, trong cùng một chuỗi mà nó bỏ qua. Nhưng về mặt Invoke
/ BeginInvoke
độ tương phản của bạn trong câu hỏi, tôi ' Tôi rất tiếc phải nói rằng bạn nên ngừng làm điều đó từ lâu.
await
chức năng có liên quan. Nó chỉ là rất nhiều đường cú pháp để tiếp tục đi qua . Có thể có một số cải tiến không liên quan đến WinForms được cho là có ích? Tuy nhiên, điều đó sẽ thuộc khuôn khổ .NET và không phải là C #.