2012-11-01 13 views
5

Tôi có một Task mà tôi đang bắt đầu và muốn chờ để hoàn thành trong một ứng dụng WPF. Bên trong nhiệm vụ này, tôi gọi một số Action trên điều phối viên.Task.Wait vs Task.RunSyncronously nơi tác vụ đã gọi tới WPF Dispatcher.Invoke

Nếu tôi sử dụng Task.Wait(), dường như nó treo như thể phương pháp chưa bao giờ kết thúc. Ngoài ra, các điểm ngắt bên trong Dispatcher.Invoke không bao giờ được nhấn.

Nếu tôi sử dụng Task.RunSyncronously(), có vẻ như nó hoạt động chính xác và các điểm ngắt bên trong Bộ điều phối bị trúng.

Tại sao có sự khác biệt?

mẫu mã dưới đây:

public void ExampleMethod() 
{ 
    // When doing the following: 
    var task = new Task(LoadStuff); 

    // This never returns: 
    task.Start(); 
    task.Wait(); 

    // This version, however, does: 
    task.RunSyncronously(); 
} 

private void LoadStuff() 
{ 
    ObservableCollection<StuffObj> stuff = Stuff.Load(arg1, true); 

    DispatchHelper.RunOnDispatcher(() => 
    { 
     ... 
    }); 
} 

public static class DispatchHelper 
{ 
    public static void RunOnDispatcher(Action action) 
    { 
     Application.Current.Dispatcher.Invoke(action); 
    } 
}  

Trả lời

5

Vâng, có một sự khác biệt lớn. Nếu bạn sử dụng RunSyncronously bạn chỉ cần chạy tác vụ trong chuỗi giao diện người dùng. Nếu bạn khởi động nó trong chuỗi nền và chúng tôi Wait thì mã đang chạy trong chuỗi nền và chuỗi giao diện người dùng bị chặn. Nếu mã trong tác vụ đó đang gọi đến chuỗi giao diện người dùng và chuỗi giao diện người dùng đang bị chặn (bởi Wait) thì bạn đã tạo một bế tắc và ứng dụng sẽ vẫn bị đóng băng.

Lưu ý rằng nếu bạn đã sử dụng, RunSyncronously trên tác vụ đó từ một chuỗi không phải giao diện người dùng và chuỗi giao diện người dùng đã bị chặn bởi một thứ khác, bạn sẽ vẫn thấy bế tắc.

Bây giờ, khi cho những gì bạn nên làm, có thực sự hai lựa chọn ở đây:

  1. Nhiệm vụ tự nó không thực sự phải mất một thời gian dài, và nó thực sự cần chạy trong thread UI hơn trong chuỗi nền. Chuỗi giao diện người dùng sẽ không bị đóng băng (tạm thời) đủ lâu để trở thành sự cố khi thực hiện tất cả công việc này trực tiếp trong giao diện người dùng. Nếu đây là trường hợp, bạn có lẽ thậm chí không nên làm cho nó một nhiệm vụ, chỉ cần đặt mã trong một phương pháp và gọi phương pháp.

  2. Tác vụ mất nhiều thời gian để chạy và sau đó cập nhật giao diện người dùng sau khi thực hiện công việc đó. Nếu đó là trường hợp thì điều quan trọng là nó không phải là RunSyncronously nhưng bắt đầu trong một chủ đề nền. Để ngăn chặn toàn bộ ứng dụng của bạn khỏi bế tắc, điều đó có nghĩa là bạn cần phải không chặn chuỗi giao diện người dùng thông qua cuộc gọi Wait. Những gì bạn cần làm nếu bạn có một số mã mà bạn muốn chạy sau khi tác vụ kết thúc, là thêm một sự tiếp tục vào nhiệm vụ. Trong C# 4.0, điều này có thể được thực hiện bằng cách gọi ContinueWith về nhiệm vụ và thêm vào một đại biểu để chạy. Trong C# 5.0+, bạn có thể thay thế await trên tác vụ có liên quan (thay vì Wait nhập vào nó, điều này thực sự là một sự khác biệt lớn) và nó sẽ tự động kết nối phần còn lại của phương thức để tiếp tục cho bạn (có hiệu lực là cú pháp đường cho một cuộc gọi rõ ràng ContinueWith, nhưng nó là một trong rất hữu ích).

+0

Ý của bạn là .NET 4.5, chưa có .NET 5. Ngoài ra, các phương thức không đồng bộ tạo ra một máy trạng thái thay vì sử dụng ContinueWith. –

+0

@BrianReichle Tôi có nghĩa là C#, chứ không phải .NET, nhưng có, nó đã sai. Theo như 'async', có nó tạo ra một máy trạng thái, và máy trạng thái đó thêm chính nó như là một sự tiếp tục của nhiệm vụ đang được chờ đợi. Máy trạng thái là cơ chế theo đó nó biết cách lấy nơi nó rời đi khi tiếp tục chạy, nhưng việc thêm một sự tiếp tục vào nhiệm vụ đang chờ đợi là cách nó chạy * bất cứ thứ gì * khi 'Task' kết thúc. – Servy

+0

Máy trạng thái lên lịch lại thông qua thiết bị chờ được trả về bởi 'GetAwaiter()', không phải 'ContinueWith' (điều này cho phép chúng ta chờ đợi những thứ khác ngoài nhiệm vụ). Cả hai đều gọi thông qua một thực thi phổ biến, nhưng 'task.GetAwaiter(). OnCompleted (...)' không gọi 'ContinueWith' (đặc biệt, trước đây tránh tạo một nhiệm vụ mới). Nhưng tôi cho rằng điều này chỉ là nhận được thông tin chi tiết, hầu hết mọi người sẽ không quan tâm đến chi tiết thực hiện :) –