2014-09-03 46 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()甚至不会开始,将没有上下文切换(这是一种优化的,这同样适用于取消的任务),所以你异步方法将是同步的

同时延续版本实际上将并行线程上开始延续。