2013-01-09 23 views
81

Tôi đang tạo một ứng dụng máy tính để bàn wpf đơn giản. Giao diện người dùng chỉ có một nút và mã trong tệp .cs như thế nào.Bắt đầu có thể không được gọi trên một nhiệm vụ theo kiểu lời hứa. ngoại lệ đang đến

private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
    FunctionA(); 
} 

public void FunctionA() 
{ 
    Task.Delay(5000).Start(); 
    MessageBox.Show("Waiting Complete"); 
} 

Nhưng đáng ngạc nhiên dòng Task.Delay(5000).Start(); là ném một InvalidOperationException:

Bắt đầu có thể không được gọi vào một nhiệm vụ hứa hẹn mang phong cách.

Bất kỳ ai có thể giúp tại sao nó giống như thế này không?

Trả lời

120

Bạn nhận được lỗi đó vì lớp Task đã bắt đầu công việc trước khi gửi cho bạn. Bạn chỉ nên gọi Start về tác vụ mà bạn tạo bằng cách gọi hàm tạo của nó và bạn thậm chí không nên làm điều đó trừ khi bạn có lý do thuyết phục để không bắt đầu tác vụ khi tạo; nếu bạn muốn nó bắt đầu ngay lập tức, bạn nên sử dụng Task.Run hoặc Task.Factory.StartNew để tạo và bắt đầu một Task mới.

Vì vậy, bây giờ chúng tôi biết chỉ cần loại bỏ điều đó pesky Start. Bạn sẽ chạy mã của bạn và thấy rằng hộp thông báo được hiển thị ngay lập tức, không phải 5 giây sau đó, có chuyện gì với điều đó?

Vâng, Task.Delay chỉ cung cấp cho bạn một tác vụ sẽ được hoàn thành sau 5 giây. Nó không ngừng thực hiện chủ đề trong 5 giây. Những gì bạn muốn làm là có một số mã được thực hiện sau khi nhiệm vụ đó kết thúc. Đó là những gì ContinueWith dành cho. Nó cho phép bạn chạy một số mã sau khi một công việc nhất định được thực hiện:

public void FunctionA() 
{ 
    Task.Delay(5000) 
    .ContinueWith(t => 
    { 
     MessageBox.Show("Waiting Complete"); 
    }); 
} 

Điều này sẽ hoạt động như mong đợi.

Chúng tôi cũng có thể tận dụng await từ khóa C# 5.0 để thêm continuations dễ dàng hơn:

public async Task FunctionA() 
{ 
    await Task.Delay(5000); 
    MessageBox.Show("Waiting Complete"); 
} 

Trong khi giải thích đầy đủ về những gì đang xảy ra ở đây là ngoài phạm vi của câu hỏi này, kết quả cuối cùng là một phương pháp hoạt động rất giống với phương pháp trước; nó sẽ hiển thị một hộp thông báo 5 giây sau khi bạn gọi phương thức, nhưng chính phương thức đó sẽ trả về [gần như] ngay lập tức trong cả hai trường hợp. Điều đó nói rằng, await là rất mạnh mẽ, và cho phép chúng tôi viết các phương pháp có vẻ đơn giản và dễ hiểu, nhưng điều đó sẽ khó khăn hơn và phức tạp hơn để viết trực tiếp bằng cách sử dụng ContinueWith. Nó cũng đơn giản hóa rất nhiều việc xử lý lỗi, lấy ra rất nhiều mã soạn sẵn.

1

Hãy thử điều này.

private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
    FunctionA(); 
} 

public async void FunctionA() 
{ 
    await Task.Delay(5000); 
    MessageBox.Show("Waiting Complete"); 
} 
-4

Như Servy nói, nhiệm vụ đã bắt đầu, vì vậy tất cả các bạn còn lại để làm là chờ đợi cho nó (.Wait()):

private void Button_Click_2(object sender, RoutedEventArgs e) 
{ 
    FunctionA(); 
} 
public void FunctionA() 
{ 
    Task.Delay(5000).Wait(); 
    MessageBox.Show("Waiting Complete"); 
} 
+0

Calling 'Wait() 'trên một nhiệm vụ sẽ chặn thread hiện tại cho đến khi nhiệm vụ được giải quyết. Đó là gần như không bao giờ những gì bạn muốn xảy ra. – Jeremy

+0

@Jeremy: Thật vậy, đáng chú ý đến hành vi mà bạn đã đề cập, nhưng trong trường hợp này, FunctionA của anh ấy đã chặn luồng hiện tại, vì vậy tôi cho rằng anh ấy đang tìm cách xác định thời điểm tác vụ đã hoàn thành. Để làm rõ sự khác biệt giữa Chờ và không đồng bộ (đối với người đọc trong tương lai), vui lòng đọc [link] này (http://stackoverflow.com/questions/9519414/whats-the-difference-between-task-start-wait-and-async- đang chờ đợi) – Sergiu

Các vấn đề liên quan