48

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

  1. 同步:

    IOMethod()
    
  2. 异步:

    BeginIOMethod()
    EndIOMethod()
    

那么当我们以如下所示的不同方式执行该方法时,在资源利用率方面的性能差异是什么?

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

2 回答 2

79
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,那么您应该使用它。

于 2013-07-02T19:14:45.283 回答
1

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

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

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

这可以通过使用TaskCompletionSource来完成。Task.Factory.FromAsync可以创建 a 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 的回答。)

于 2013-07-02T17:49:30.530 回答