2014-09-03 44 views
3

我的await關鍵字的理解是,繼await合格聲明代碼爲聲明的延續運行,一旦它完成。SemaphoreSlim.WaitAsync繼續碼

因此,下面的兩個版本應該產生相同的輸出:

public static Task Run(SemaphoreSlim sem) 
    { 
     TraceThreadCount(); 
     return sem.WaitAsync().ContinueWith(t => 
     { 
      TraceThreadCount(); 
      sem.Release(); 
     }); 
    } 

    public static async Task RunAsync(SemaphoreSlim sem) 
    { 
     TraceThreadCount(); 
     await sem.WaitAsync(); 
     TraceThreadCount(); 
     sem.Release(); 
    } 

但他們沒有!

下面是完整的程序:

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace CDE 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      try 
      { 
       var sem = new SemaphoreSlim(10); 
       var task = Run(sem); 

       Trace("About to wait for Run."); 

       task.Wait(); 

       Trace("--------------------------------------------------"); 
       task = RunAsync(sem); 

       Trace("About to wait for RunAsync."); 

       task.Wait(); 
      } 
      catch (Exception exc) 
      { 
       Console.WriteLine(exc.Message); 
      } 
      Trace("Press any key ..."); 
      Console.ReadKey(); 
     } 

     public static Task Run(SemaphoreSlim sem) 
     { 
      TraceThreadCount(); 
      return sem.WaitAsync().ContinueWith(t => 
      { 
       TraceThreadCount(); 
       sem.Release(); 
      }); 
     } 

     public static async Task RunAsync(SemaphoreSlim sem) 
     { 
      TraceThreadCount(); 
      await sem.WaitAsync(); 
      TraceThreadCount(); 
      sem.Release(); 
     } 

     private static void Trace(string fmt, params object[] args) 
     { 
      var str = string.Format(fmt, args); 
      Console.WriteLine("[{0}] {1}", Thread.CurrentThread.ManagedThreadId, str); 
     } 
     private static void TraceThreadCount() 
     { 
      int workerThreads; 
      int completionPortThreads; 
      ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThreads); 
      Trace("Available thread count: worker = {0}, completion port = {1}", workerThreads, completionPortThreads); 
     } 
    } 
} 

這裏是輸出:

[9] Available thread count: worker = 1023, completion port = 1000 
[9] About to wait for Run. 
[6] Available thread count: worker = 1021, completion port = 1000 
[9] -------------------------------------------------- 
[9] Available thread count: worker = 1023, completion port = 1000 
[9] Available thread count: worker = 1023, completion port = 1000 
[9] About to wait for RunAsync. 
[9] Press any key ... 

我缺少什麼?

+0

您有兩種方法之間的一個非常重要的區別差異,你的第一個方法更相當於擁有'等待sem.WaitAsync()ConfigureAwait(假);'你的第二個方法。一旦你得到的代碼行爲相同,通過拖延像答案建議嘗試運行你的測試程序在一個按鈕的OnClick,而不是在控制檯的應用程序。 – 2014-09-03 22:28:44

回答

5

async-await優化您正在等待的任務何時已完成(當信號量設置爲10時,只有一個線程正在使用它)。在那種情況下,線程只是同步進行。

你可以看到,通過增加一個實際的異步操作RunAsync,看看它是如何改變使用(這將是行爲時,你的信號是空的,調用者實際需要的等待異步)線程池中的線程:

public static async Task RunAsync(SemaphoreSlim sem) 
{ 
    TraceThreadCount(); 
    await Task.Delay(1000); 
    await sem.WaitAsync(); 
    TraceThreadCount(); 
    sem.Release(); 
} 

您也可以進行此更改Run並使其同步執行的延續,並得到了相同的結果。你RunAsync(線數明智):

public static Task Run(SemaphoreSlim sem) 
{ 
    TraceThreadCount(); 
    return sem.WaitAsync().ContinueWith(t => 
    { 
     TraceThreadCount(); 
     sem.Release(); 
    }, TaskContinuationOptions.ExecuteSynchronously); 
} 

輸出:

[1] Available thread count: worker = 1023, completion port = 1000 
[1] Available thread count: worker = 1023, completion port = 1000 
[1] About to wait for Run. 
[1] -------------------------------------------------- 
[1] Available thread count: worker = 1023, completion port = 1000 
[1] Available thread count: worker = 1023, completion port = 1000 
[1] About to wait for RunAsync. 
[1] Press any key ... 

重要提示:當它說,async-await充當延續它更多的比喻。這些概念之間有幾個關鍵的區別,特別是關於SynchronizationContext s。 async-await自動地保留當前的背景下(除非你指定ConfigureAwait(false)),這樣你就可以在環境中的事項(UI,ASP.Net等),安全地使用它。關於同步上下文的更多信息here

+1

如果他在一個帶'SynchronizationContext'的環境中運行,那麼'ExecuteSynchronously'使'Run'行爲與'RunAsync'行爲不同(我可能是錯誤的,如果我想請告訴我,我想學習)編輯:我看到你最新的忍者編輯(一個5分鐘的編輯)地址。 – 2014-09-03 22:36:17

+0

其實在行爲上有一點不同。 Async方法會將上下文切換到原始方式,因此TraceThreadCount()的第二個調用將始終打印與第一個調用相同的線程ID。但是延續版本將打印不同的結果,這取決於信號的狀態。在這種情況下,信號量總是打開的,所以TraceThreadCount()將在調用者的上下文中執行。但是如果我們暫時封鎖信號量,結果將會不同。 – 2014-09-03 22:39:39

0

他們不會因爲當你調用異步方法,它是立即開始。所以,只要你的信號沒有被鎖定,WaitAsync()甚至不會開始,將沒有上下文切換(這是一種優化的,這同樣適用於取消的任務),所以你異步方法將是同步的

同時延續版本實際上將並行線程上開始延續。