假设我们有一个I / O绑定方法(例如进行DB调用的方法)。此方法可以同步和异步运行。也就是说,
同步:
IOMethod()
异步:
BeginIOMethod()
EndIOMethod()
然后,当我们以不同的方式执行方法时,如下所示,在资源利用方面的性能差异是什么?
var task = Task.Factory.StartNew(() => { IOMethod(); });
task.Wait();
var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... );
task.Wait();
答案 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的回答。)