创建线程-Task.Factory.StartNew与new Thread()


102

我只是在学习.Net 4中的新线程和并行库

在过去,我将像这样创建一个新线程(作为示例):

DataInThread = new Thread(new ThreadStart(ThreadProcedure));
DataInThread.IsBackground = true;
DataInThread.Start();

现在我可以做:

Task t = Task.Factory.StartNew(() =>
{
   ThreadProcedure();
});

有什么区别?

谢谢


1
您需要担心线程池调度程序的工作原理。它可以带来很大的不同,但是这完全取决于您在线程内部实际执行的操作。
汉斯·帕桑

Answers:


79

有一个很大的不同。任务是在ThreadPool上安排的,甚至可以适当地同步执行。

如果您有长期的后台工作,则应使用正确的“任务选项”进行指定。

与更明确的线程处理相比,您应该更喜欢Task Parallel Library,因为它更加优化。另外,您还有更多功能,例如Continuation。


5
不,不是。它只是开始任务。这可以将任务排入线程池或同步执行。TPL旨在使您摆脱自己管理线程/并发的
麻烦,

10
有TaskCreationOptions.LongRunning选项将始终创建另一个线程,但要点是为什么您需要另一个线程?如果您只是想并行执行某项操作(在Task运行时Main做某事),最好让优化的库来决定如何利用系统资源(如线程)以最有效的方式执行此操作。
sanosdole

3
此msdn文章介绍了如何计划任务。它涵盖了长时间运行和内联(同步执行)。msdn.microsoft.com/en-us/library/dd997402.aspx
sanosdole 2011年

2
@sming关键是要同时处理(不阻止UI),而不是要新线程。ThreadPool不会阻止UI线程,但是管理后台线程比通过创建线程手动执行要高效得多。这就是TPL引入的思维过程的变化。不要想线程,要考虑并发任务。
sanosdole,2014年

4
@sming抱歉,这句话有点太粗略了。任务的同步执行称为内联。从UI线程在线程池(默认调度程序)上调度任务时,将不会发生。仅当环境计划程序('TaskScheduler.Current')与您在其上调用'.Wait()'的任务的计划程序相同时,才会发生这种情况。由于“ .Wait()”正在阻塞,因此无论如何它将阻塞UI。简短:不要调用wait,它将不会同步执行。
sanosdole 2014年

73

该任务为您提供了任务API的所有优点:

  • 添加延续(Task.ContinueWith
  • 等待多个任务完成(全部或全部)
  • 捕获任务中的错误并在以后进行查询
  • 捕获取消(并允许您指定取消开始)
  • 可能具有返回值
  • 在C#5中使用await
  • 更好地控制调度(如果要长时间运行,请在创建任务时说出这一点,以便任务调度器可以将其考虑在内)

请注意,在两种情况下,都可以通过方法组转换使代码更简单:

DataInThread = new Thread(ThreadProcedure);
// Or...
Task t = Task.Factory.StartNew(ThreadProcedure);

8
+1。ThreadTask(我有一篇详细的博客文章)相比,我想补充一点。我在大瀑布城开发日(Daily Day)进行了“在现实世界中使用任务”类演讲。这个话题称为“线程已死”,因为不再需要Thread(除非您实现TaskScheduler)。
斯蒂芬·克莱里

@StephenCleary,我想您的意思Thread是当它用作后台线程时已经死了?
退潮

1
@ebb:不,我的立场在我的第一个评论中更强。没有什么Thread可以BackgroundWorker做的更好,Task而适当的不能做的更好TaskScheduler
斯蒂芬·克莱里

1
@StephenCleary,如何在不使用的情况下创建专用线程Thread
退潮

4
@ebb:我不清楚“专用线程”。如果要Task在特定线程上运行,请使用适当的TaskScheduler-例如,AsyncContextThread。但是,这通常不是必需的。的SynchronizationContextThreadPoolConcurrentExclusiveSchedulerPair调度足以满足大多数的方案。
斯蒂芬·克莱里

12

在第一种情况下,您只是在启动新线程,而在第二种情况下,您在线程池中输入。

线程池的工作是共享和回收线程。它可以避免每次我们需要创建新线程时都浪费几毫秒的时间。

有几种进入线程池的方法:

  • 像您一样使用TPL(任务并行库)
  • 通过调用ThreadPool.QueueUserWorkItem
  • 通过在委托上调用BeginInvoke
  • 当您使用BackgroundWorker时

1

您的第一段代码告诉CLR为您创建一个线程(例如T),该线程可以作为后台运行(调度T时使用线程池线程)。简而言之,您明确要求CLR创建一个线程以供您执行操作,并在线程上调用Start()方法以启动。

您的第二个代码块执行相同的操作,但委派(隐式地移交)创建线程(后台运行,又在线程池中运行)的责任,并通过Task Factory实现中的StartNew方法启动线程。

这是给定代码块之间的快速区别。话虽这么说,但您可以在Google上找到其他详细的区别,也可以从我的其他贡献者那里看到其他答案。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.