在C#中,Task<T>与异步委托有何区别?

13

我有这个简单的方法:

static int Work (string s) { return s.Length; }

我可以用以下方式运行它:

Task<string> task = Task.Factory.StartNew<int> (() => Work ("lalala") );
...
int result = task.Result;

或者用这个:

Func<string, int> method = Work;
IAsyncResult myIasync= method.BeginInvoke ("lalala", null, null);
...
int result = method.EndInvoke (myIasync);
  • 它们都使用线程池线程。
  • 在读取值时,它们都等待执行完成。
  • 它们都会将任何异常重新抛出给调用者。

我应该在什么时候使用它们?


我认为Task更好,因为它是更新的,但我几乎不相信这个微小的代码会有任何区别。 - Amiram Korach
2个回答

20

第二种形式使用 IAsyncResult,它显著地更老,而且功能较弱。Task<T>在.NET 4中引入,现在是表示异步操作的首选方式。它的使用要简单得多,特别是在支持“异步函数”的C#5中,您可以以非阻止方式等待任务(或其他异步操作)。

使用Task代替调用BeginInvoke可能不会改变操作本身的执行方式(尽管它给您提供了更多的调度选项等),但从想要“观察”操作、使用结果、等待多个任务、处理故障等代码的角度来看,它会带来巨大的影响。

如果可能使用C#5(无论是.NET 4.5还是.NET 4加上async targeting pack),在管理异步操作时它会让您的生活更加轻松。这是未来的发展方向:)


假设任务执行需要很长时间。它会占用线程池中的一个线程直到任务完成吗(这样线程池就只有[n-1]个线程)?还是任务被委派给非线程池线程,只有当它完成后才返回到线程池线程...? - user998026
1
@AutoExec.Bat:如果您使用“Task”,则可以告诉它这是一个长时间运行的任务,在这种情况下,它将使用非线程池线程。不清楚您所说的“它返回到线程池线程”的意思。什么会返回到线程池线程? - Jon Skeet
需要创建一个任务,这是一个长时间运行的任务。该任务可以使用线程池线程执行,或者可以将其转移到非线程池线程(因此现在线程回到池中-为了服务其他请求),当任务完成时(在非线程池线程中)-它发出信号:“嘿,我完成了工作”,控制权回到线程池线程。(这个想法是为了节省线程池线程的浪费)。所以我的问题是:在这种情况下,_Task vs IAsync_如何工作...(实际工作是由线程池还是非线程池线程完成的?) - user998026
现在我已经看到(正如你所提到的)任务具有“LongRunning”标志,它确实可以做到这一点。因此,剩下的问题是关于异步委托。谢谢 :-) - user998026
1
@AutoExec.Bat:“控制权已经回到线程池线程” - 什么控制权?我认为您需要退后一步,思考每个线程实际上正在做什么。我相信在委托上使用BeginInvoke始终会使用线程池线程。 - Jon Skeet
你的最后一句话回答了我的问题。顺便说一下:我在谈论异步操作本身是如何工作的。当异步操作完成时,它将调用此方法,并提供与之前相同的IAsyncResult。此时,您可以从中检索状态对象,或将IAsyncResult传递给EndInvoke。如果它还没有完成,它将阻塞直到完成,但如果已经结束-操作将继续进行。 - user998026

0

如果符合您的需求,我建议使用更加优雅且较新的任务(.Net 4)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接