Bắt đầu có thể không được gọi trong một nhiệm vụ kiểu hứa. ngoại lệ đang đến


108

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ư.

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 Task.Delay(5000).Start();là dòng đang ném một InvalidOperationException:

Bắt đầu có thể không được gọi trong một nhiệm vụ kiểu hứa.

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

Câu trả lời:


171

Bạn nhận được lỗi đó vì Tasklớp đã bắt đầu nhiệm vụ trước khi giao nó cho bạn. Bạn chỉ nên gọi Startmột tác vụ mà bạn tạo bằng cách gọi hàm khởi 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 bạn tạo nó; nếu bạn muốn nó bắt đầu ngay lập tức, bạn nên sử dụng Task.Runhoặc Task.Factory.StartNewđể tạo và bắt đầu một cái mới Task.

Vì vậy, bây giờ chúng ta biết cách loại bỏ sự phiền phức đó Start. Bạn sẽ chạy mã của mình 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ậy?

Chà, Task.Delaychỉ giao cho bạn một nhiệm vụ sẽ hoàn thành sau 5 giây. Nó không ngừng thực hiện chuỗi trong 5 giây. Những gì bạn muốn làm là có một số mã được thực thi sau khi tác vụ đó kết thúc. Đó là những gì ContinueWithdành cho. Nó cho phép bạn chạy một số mã sau khi hoàn thành một nhiệm vụ cụ thể:

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 awaittừ khóa của C # 5.0 để thêm các đoạn liên tục dễ dàng hơn:

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

Trong khi lời giải thích đầy đủ về những gì đang xảy ra ở đây nằm 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 thức 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 bản thân 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, awaitnó rất mạnh mẽ và cho phép chúng ta viết các phương thức có vẻ đơn giản và dễ hiểu, nhưng điều đó sẽ khó hơn và lộn xộn hơn nhiều nếu sử dụng ContinueWithtrực tiếp. Nó cũng giúp đơ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

Thử cái 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ả những gì bạn còn lại phải làm là đợi nó (.Wait ()):

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

1
Gọi Wait()một tác vụ sẽ chặn luồng hiện tại cho đến khi tác vụ được giải quyết. Đó gần như không bao giờ là điều bạn muốn xảy ra.
Jeremy

1
@Jeremy: Thật vậy, cần 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 chỉ đang tìm cách xác định khi nào nhiệm vụ đã hoàn thành. Để làm rõ sự khác biệt giữa Wait và async (dành cho độc giả trong tương lai), vui lòng đọc liên kết
Sergiu
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.