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

cod*_*fun 35 asynchronous task-parallel-library

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

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

从我的理解来看,添加TaskCompletionSource到你的一个类似乎没有做太多的编码异步; 如果您仍在执行同步代码,则对代码的调用将被阻止.我认为微软API也是如此.例如,假设在DownloadStringTaskAsync关闭的WebClient类,它们最初在做任何安装/同步代码将被阻塞.您正在执行的代码必须在某个线程上运行,无论是当前线程还是您必须分拆一个新线程.

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

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

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

Ste*_*ary 59

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

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

我有一篇博文" 创建任务",讨论了创建Task实例的不同方法.它是从async/ awaitperspective(不是TPL的角度)编写的,但它仍然适用于此处.

另见Stephen Toub的优秀帖子:


Max*_*eev 6

我喜欢http://tutorials.csharp-online.net/TaskCompletionSource中提供的解释

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

前两段如下

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

TaskCompletionSource允许您从任何启动并在稍后完成的操作中创建任务.它的工作原理是为您提供一个"奴隶"任务,您可以通过指示操作完成或故障来手动驱动.这是I/O绑定工作的理想选择:您可以获得任务的所有好处(具有传播返回值,异常和延续的能力),而不会在操作期间阻塞线程.

要使用TaskCompletionSource,您只需实例化该类.它公开了一个Task属性,该属性返回一个任务,您可以在该任务上等待并附加continuation - 就像任何其他任务一样.但是,该任务完全由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();
 ... 
}
Run Code Online (Sandbox Code Playgroud)

调用任何这些方法都会发出任务信号,将其置于完成,故障或取消状态(我们将在"取消"部分中介绍后者).你应该只调用其中一种方法:如果再次调用,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
Run Code Online (Sandbox Code Playgroud)

其他有趣的报价

TaskCompletionSource的真正强大之处在于创建不会占用线程的任务.

..以及后来

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

  • 很好的解释 (2认同)