TaskCompletionSource - 尝试理解无线程异步工作

时间:2012-05-02 18:40:04

标签: asynchronous task-parallel-library

我试图了解TaskCompletionSource的目的及其与异步/无线程工作的关系。我想我有一般的想法,但我想确保我的理解是正确的。

我首先开始研究任务并行库(TPL),以确定是否有一种很好的方法来创建自己的无线/异步工作(比如说你试图提高ASP.NET站点的可伸缩性)以及理解TPL看起来将来会非常重要(async / await)。这让我想到了TaskCompletionSource

根据我的理解,将TaskCompletionSource添加到您的某个类中似乎并没有真正做到使您的编码异步;如果您仍在执行同步代码,则对代码的调用将被阻止。我认为微软API也是如此。例如,在DownloadStringTaskAsync类的WebClient之内,最初他们正在执行的任何设置/同步代码都将被阻止。您正在执行的代码必须在某个线程上运行,无论是当前线程还是您必须分拆一个新线程。

因此,当您从Microsoft调用其他TaskCompletionSource调用时,在自己的代码中使用async,这样您的类的客户端就不必为您的类创建一个新线程来阻止

不确定Microsoft如何在内部执行异步API。例如,对于.Net 4.5,async有一个新的SqlDataReader方法。我知道有IO完成端口。我认为这可能是大多数C#开发人员不会使用的低级抽象(C ++?)。不确定IO完成端口是否适用于数据库或网络调用(HTTP),或者它是否仅用于文件IO。

所以问题是,我理解正确吗?是否有某些我错误表示的内容?

2 个答案:

答案 0 :(得分:58)

TaskCompletionSource用于创建不执行代码的Task个对象。

微软新的异步API使用它们很多 - 任何时候都有基于I / O的异步操作(或其他非基于CPU的异步操作,如超时)。此外,您编写的任何async Task方法都将使用TCS完成其返回的Task

我有一篇博文Creating Tasks,讨论了创建Task个实例的不同方法。它是从async / await角度(不是TPL角度)编写的,但它仍然适用于此。

另见Stephen Toub的优秀帖子:

答案 1 :(得分:5)

我喜欢http://tutorials.csharp-online.net/TaskCompletionSource

中提供的解释

(对不起,链接可能已经死了)

前两段如下

  

我们已经看到Task.Run如何创建一个在一个上运行委托的任务   汇集(或非汇集)线程。另一种创建任务的方法是   TaskCompletionSource。

     

TaskCompletionSource允许您从任何操作中创建任务   一段时间后开始并结束。它的工作原理是给你一个“奴隶”   您手动驱动的任务 - 指示操作何时完成   或者是错误。这是I / O绑定工作的理想选择:您可以获得所有好处   任务(具有传播返回值的能力,异常,   和持续)在没有阻塞线程的持续时间   操作

     

要使用TaskCompletionSource,您只需实例化该类。它   公开一个Task属性,该属性返回您可以等待的任务   并附加延续 - 就像任何其他任务一样。任务,   但是,完全由TaskCompletionSource对象控制   以下方法:

public class TaskCompletionSource<TResult> 
{ 
 public void SetResult(TResult result); 
 public void SetException (Exception exception); 

 public void SetCanceled();   
 public bool TrySetResult (TResult result); 
 public bool TrySetException (Exception exception); 
 public bool TrySetCanceled();
 ... 
}
  

调用这些方法中的任何一个都会发出任务信号,并将其放入   完成,故障或取消状态(我们将覆盖后者   “取消”部分。你应该调用其中一种方法   恰好一次:如果再次调用,则为SetResult,SetException或SetCanceled   将抛出异常,而Try *方法返回false。

     

以下示例在等待五秒后打印42:

var tcs = new TaskCompletionSource<int>();
new Thread (() =>     {
                       Thread.Sleep (5000); 
                       tcs.SetResult (42); 
                      })    
           .Start();   
Task<int> task = tcs.Task;    // Our "slave" task. 
Console.WriteLine(task.Result);  // 42

其他有趣的引语

  

TaskCompletionSource的真正强大之处在于创建不能完成的任务   捆绑线程。

..以及稍后

  

我们在没有线程的情况下使用TaskCompletionSource意味着一个线程   只有在继续开始时才会使用,五秒钟后。我们   可以通过一次启动10,000个这样的操作来证明这一点   没有错误或资源消耗过多: