Task.Factory.StartNew vs Task.Factory.FromAsync
本文关键字:Task Factory FromAsync StartNew vs | 更新日期: 2023-09-27 18:20:48
让我们假设我们有一个I/O绑定方法(比如一个进行DB调用的方法)。此方法既可以在中同步运行,也可以在中异步运行。也就是说,
-
同步:
IOMethod()
-
异步:
BeginIOMethod() EndIOMethod()
然后,当我们以如下所示的不同方式执行该方法时,在资源利用率方面的性能差异是什么?
var task = Task.Factory.StartNew(() => { IOMethod(); }); task.Wait();
var task = Task.Factory.FromAsync(BeginIOMethod, EndIOMethod, ... ); task.Wait();
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)将(很可能)导致.NET线程池处理您的Task
。
(2) 将使用BeginIOMethod
/EndIOMethod
对本机使用的任何机制来处理异步部分,异步部分可能涉及也可能不涉及.NET线程池。
例如,如果您的BeginIOMethod
正在通过internet发送TCP消息,并且稍后收件人将向您发送TCP消息作为响应(由EndIOMethod
接收),则.NET线程池不会提供该操作的异步性质。正在使用的TCP库提供异步部分。
这可以通过使用TaskCompletionSource
类来实现。Task.Factory.FromAsync
可以创建一个TaskCompletionSource<T>
,返回其Task<T>
,然后使用EndIOMethod
作为触发器,将Result
放入调用时从Task.Factory.FromAsync
返回的Task<T>
中。
资源利用率方面的性能差异是什么?
(1)和(2)之间的区别主要在于.NET线程池是否要添加其工作负载。通常,如果只有Begin...
/End...
对,则正确的做法是选择Task.Factory.FromAsync
,否则选择Task.Factory.StartNew
。
如果您使用的是C#5.0,那么您应该使用非阻塞await task;
而不是task.Wait();
。(参见svick的答案。)