3

我想了解異步編程好一點的基礎,所以我創建了下面的代碼片段:爲什麼Task.Wait()的行爲與Console.WriteLine()無關?

private void TaskContinuations() 
{ 
    // Task for counting prime numbers 
    Task<int> primeNumberTask = Task.Run(() => 
     Enumerable.Range(2, 3000000) 
     .Count(n => 
      Enumerable.Range(2, (int)Math.Sqrt(n) - 1) 
      .All(i => n % i > 0))); 

    var awaiter = primeNumberTask.GetAwaiter(); 

    // primeNumberTask.Wait(); // Option 1: Waiting but not printing (rather unexpected here) 

    awaiter.OnCompleted(() => 
    { 
     var result = awaiter.GetResult(); 
     Console.WriteLine(result); 
    }); 

    //primeNumberTask.Wait(); // Option 2: Waiting but not printing (kind of expected here) 

    Console.Read(); // Option 3: Works and prints as expected 
} 

我明白,我必須防止主UI任務從停止,以防止後臺任務從被終止。因此,Console.Read();會阻止UI線程,並且如果在素數編號任務完成之前未按某個鍵,則控制檯中的輸出正確爲216816。所有清除,直到這裏。

所以我認爲調用Wait();會產生同樣的效果(至少在OnCompleted回調之前調用),因爲主UI任務會阻塞並等待質數任務完成。這確實發生了,但是在這兩種情況下(選項1和選項2),主線程都會等待,然後終止而不輸出任何結果。

這是爲什麼?我的行爲的期望是以下(這可能是掉了一點,所以我的問題)

  1. 主UI線程等待/爲任務阻塞,直到它完成,訂閱完成的任務,所以執行回調立即,輸出打印是216816。但那不會發生!

  • 主UI線程等待/塊,並且當任務完成它執行OnCompleted之前立即如此做。所以這裏沒有產出。
  • 那麼我在這裏想念什麼,有人可以對我點燈?

    +0

    當您取消註釋'primeNumberTask.Wait();'時,是否還有'Console.Read();'。如果你不這樣做 - 程序已經返回了,它有機會輸出任何東西。 – zerkms

    +1

    [相關](http://stackoverflow.com/a/33310175/335858) – dasblinkenlight

    +0

    沒有,如果我保持Console.Read();它確實在所有情況下打印'216816'。給出的選項是'exclusive';-) – Anytoe

    回答

    1

    首先,according MSDN,你不應該使用OnCompleted方法,如

    此API支持產品基礎設施和不適合直接在代碼中使用。

    第二件事情是OnCompleted方法被調用,那麼,後任務完成。所以你在這裏有一個競爭條件 - 任務完成,並且.Net試圖在它之後執行該方法,但是代碼中沒有任何內容正在等待awaiter,所以程序在任何打印之前都會退出。

    應該在這種情況下用途是ContinueWith task,與等待第二個任務的代碼:

    private void TaskContinuations() 
    { 
        // Task for counting prime numbers 
        var primeNumberTask = Task.Run(() => 
         Enumerable.Range(2, 3000000) 
         .Count(n => 
          Enumerable.Range(2, (int)Math.Sqrt(n) - 1) 
          .All(i => n % i > 0))); 
    
        // continuation 
        var awaiterTask = primeNumberTask.ContinueWith(t => 
        { 
         var result = t.Result; 
         Console.WriteLine(result); 
        }); 
    
        // wait for everything to execute 
        awaiterTask.Wait(); 
    } 
    

    請注意,ContinueWith has it's own pitfalls,並應謹慎使用。關於延續技術的另一篇文章可以在here找到。

    相關問題