Task.Factory.StartNew与Task.Factory.FromAsync

时间:2013-07-02 17:45:25

标签: c# .net asynchronous task-parallel-library

假设我们有一个I / O绑定方法(例如进行DB调用的方法)。此方法可以同步和异步运行。也就是说,

  1. 同步:

    IOMethod()
    
  2. 异步:

    BeginIOMethod()
    EndIOMethod()
    
  3. 然后,当我们以不同的方式执行方法时,如下所示,在资源利用方面的性能差异是什么?

    1. var task = Task.Factory.StartNew(() => { IOMethod(); });
      task.Wait();
      
    2. var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
      task.Wait();
      

2 个答案:

答案 0 :(得分:64)

var task = Task.Factory.StartNew(() => { IOMethod(); });
task.Wait();

这将在执行IOMethod()时阻塞线程池线程,并且由于Wait()而阻塞当前线程。总阻塞线程:2。

var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.Wait();

这将(很可能)在不使用线程的情况下异步执行操作,但由于Wait(),它将阻止当前线程。总阻塞线程:1。

IOMethod();

这将在执行IOMethod()时阻止当前线程。总阻塞线程:1。

如果您需要阻止当前线程,或者阻止它对你没用,那么你应该使用它,因为尝试使用TPL实际上不会给你任何东西。

var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
await task;

这将在不使用线程的情况下异步执行操作,并且由于await,它还将等待操作异步完成。被阻止的线程总数:0。

如果您想利用异步并且可以使用C#5.0,那么这就是您应该使用的。

var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.ContinueWith(() => /* rest of the method here */);

这将在不使用线程的情况下异步执行操作,并且由于ContinueWith(),它还将等待操作异步完成。被阻止的线程总数:0。

如果您想利用异步并且不能使用C#5.0,那么应该使用这个。

答案 1 :(得分:1)

(1)将(可能)导致.NET线程池处理您的Task

(2)将使用您的BeginIOMethod / EndIOMethod对本机使用的任何机制来处理异步部分,这可能涉及或不涉及.NET线程池。

例如,如果您的BeginIOMethod正在通过互联网发送TCP消息,并且稍后收件人将向您发送TCP消息作为响应(由EndIOMethod收到),则.NET线程池不提供操作的异步性质。正在使用的TCP库提供异步部分。

这可以通过使用TaskCompletionSource class来完成。 Task.Factory.FromAsync可以创建TaskCompletionSource<T>,返回其Task<T>,然后使用EndIOMethod作为触发器,将Result放入已返回的Task<T>在致电时形成Task.Factory.FromAsync

  

资源利用率方面的性能差异是什么?

(1)和(2)之间的区别主要在于.NET线程池是否会添加其工作负载。一般来说,正确的做法是,如果您只有Task.Factory.FromAsync / Begin...对,则选择End...,否则选择Task.Factory.StartNew


如果您使用的是C#5.0,那么您应该使用非阻止await task;而不是task.Wait();。 (见svick的回答。)