2014-12-15 32 views
14

Cho phép nói rằng tôi chỉ muốn một phương thức chạy trong async.Sử dụng async theo phương pháp không đồng bộ

Vì vậy, tôi có một phương pháp async như dưới đây:

public async Task Load(){ 
    Task task1 = GetAsync(1); 
    Task task2 = GetAsync(2); 
    Task task3 = GetAsync(3); 
    var data1 = await task1; // <--Freezes here when called from GetSomethingElse() 
    var data2 = await task2; 
    var data3 = await task3; 
    ..process data.. 
} 

Và sau đó tôi đang cố gắng để gọi đó là async phương pháp trong phương pháp khác như một nhiệm vụ, và muốn cho nó phải đợi cho đến khi đoạn cụ thể của Mã số async được thực hiện. Vấn đề là nó không phải. Khi nó đạt đến await đầu tiên trong Load() nó chỉ không hoàn thành tải. Trình gỡ lỗi bị trống và không đưa ra lỗi nào khác.

Phương thức async có thể được gọi từ phương thức không async, như thế này không? Có một lý do tôi không cần nhiệm vụ cụ thể này là async, nhưng chức năng Load() tôi làm.

public void GetSomethingElse(){ 
    var task1 = Load().Wait();  
} 

Làm cách nào có thể?


tôi đã cố gắng thậm chí thay đổi phương pháp Load() sử dụng var data = task1.Wait() vv thay vì await, vẫn có sự khác biệt, không có vấn đề mà cách tôi cố gắng. Nếu bất cứ ai có thể giúp nó sẽ được đánh giá cao.

+0

thử: 'var data1 = await Task.Run (() => GetAsync (1));' –

+0

http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638. aspx –

+0

@JohnWoo Tôi đã thử 'var data1 = task1.Wait();' sẽ làm nó theo cách của bạn là bất kỳ khác nhau? –

Trả lời

11

Bạn có thể có bế tắc trên tay. Bạn đang chặn một chuỗi bằng cách sử dụng Wait() trên một công việc cần chuỗi đó để hoàn thành vì có SynchronizationContext đang được sử dụng trong ASP.Net (cũng trong môi trường GUI).

Bạn nên sử dụng ConfigureAwait(false) để yêu cầu người chờ đợi không chụp được ngữ cảnh đó. Nó đủ để làm điều đó trên await đầu tiên kể từ phần còn lại sẽ không có SynchronizationContext để nắm bắt:

public async Task Load() 
{ 
    Task task1 = GetAsync(1); 
    Task task2 = GetAsync(2); 
    Task task3 = GetAsync(3); 
    var data1 = await task1.ConfigureAwait(false); 
    var data2 = await task2; 
    var data3 = await task3; 
    //..process data. 
} 

Tuy nhiên, nó được khuyến khích để luôn luôn sử dụng ConfigureAwait trừ bạn muốn để nắm bắt những SynchronizationContext do đó, một tiêu chuẩn tốt hơn là thế này:

public async Task Load() 
{ 
    Task task1 = GetAsync(1); 
    Task task2 = GetAsync(2); 
    Task task3 = GetAsync(3); 
    var data1 = await task1.ConfigureAwait(false); 
    var data2 = await task2.ConfigureAwait(false); 
    var data3 = await task3.ConfigureAwait(false); 
    //..process data. 
} 

trong trường hợp của bạn, nơi bạn muốn tiếp tục sau khi tất cả nhiệm vụ hoàn thành, bạn nên sử dụng Task.WhenAll thay vì await trong g từng nhiệm vụ riêng biệt:

public async Task Load() 
{ 
    await Task.WhenAll(GetAsync(1), GetAsync(2), GetAsync(3)).ConfigureAwait(false); 
    // process data. 
} 

Lưu ý: làm đồng bộ trên async thường không được khuyến khích vì nó không có lợi ích (bạn đang chặn một sợi trong suốt toàn bộ hoạt động) và có thể gây bế tắc (như thế này một).

+0

Nếu 'GetAsync' trả về biến thì sao? Và bạn đang nói rằng tôi vẫn sẽ chạy vào deadlocks bằng cách sử dụng phương pháp này? –

+0

@ControlFreak No. ConfigureAwait giải quyết điều đó. Task.WhenAll chỉ thoải mái hơn trong trường hợp này. – i3arnon

+0

@ControlFreak Tôi đã cập nhật câu trả lời để làm rõ hơn. – i3arnon

1

Bạn có thể thay đổi chức năng tải của bạn như sau:

public async Task Load(){ 
    await new TaskFactory().StartNew(() => 
    { 
     Task task1 = GetAsync(1); 
     Task task2 = GetAsync(2); 
     Task task3 = GetAsync(3); 
     var data1 = await task1; // <--Freezes here when called from GetSomethingElse() 
     var data2 = await task2; 
     var data3 = await task3; 
     ..process data.. 
    }); 
} 
2

Tôi nghĩ rằng bạn có một kịch bản bế tắc cổ điển. Xem this post để biết thêm chi tiết. Khi bạn có tuyên bố await, SynchronizationContext hiện tại được lưu trữ trước khi gọi await và được khôi phục sau đó và phần còn lại của phương thức được đăng lên đó.Trong ứng dụng GUI chỉ có một luồng được liên kết với bối cảnh đó để phần còn lại của phương thức được thực hiện trên luồng GUI nhưng không thể vì Wait() là một cuộc gọi chặn chặn luồng GUI.

Hãy thử điều này thay vì:

public async Task Load(){ 
    Task task1 = GetAsync(1).ConfigureAwait(false); 
    Task task2 = GetAsync(2).ConfigureAwait(false); 
    Task task3 = GetAsync(3).ConfigureAwait(false); 

    var data1 = await task1; // <--Freezes here when called from GetSomethingElse() 
    var data2 = await task2; 
    var data3 = await task3; 
    ..process data.. 
} 

Nếu có bất kỳ đang chờ đợi bên GetAsync bạn có thể phải thêm .ConfigureAwait(false) đó là tốt.

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