【问题标题】:Creating threads - Task.Factory.StartNew vs new Thread()创建线程 - Task.Factory.StartNew vs new Thread()
【发布时间】:2013-07-16 15:37:19
【问题描述】:

我刚刚了解 .Net 4 中的新线程和并行库

过去我会像这样创建一个新线程(例如):

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

现在我可以做:

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

如果有的话有什么区别?

谢谢

【问题讨论】:

  • 您需要担心线程池调度程序的工作原理。它可以产生很大的不同,但这一切都取决于你在线程中实际做了什么。

标签: c# .net multithreading .net-4.0


【解决方案1】:

有很大的不同。任务在 ThreadPool 上调度,如果合适,甚至可以同步执行。

如果您有长时间运行的后台工作,您应该使用正确的任务选项来指定它。

您应该更喜欢任务并行库而不是显式线程处理,因为它更加优化。此外,您还有更多功能,例如继续。

【讨论】:

  • 不,它没有。它只是启动任务。这可以将任务排入线程池或同步执行。 TPL 旨在让您从自己管理线程/并发中解放出来,并为您的平台使用最好的(例如利用内核)
  • TaskCreationOptions.LongRunning 选项总是会创建另一个线程,但重点是为什么需要另一个线程?如果您只想并行执行某些操作(Main 在 Task 运行时执行某些操作),最好让优化的库决定如何利用线程等系统资源以最有效的方式执行此操作。
  • 这篇 msdn 文章介绍了如何安排任务。它涵盖了长时间运行和内联(同步执行)。 msdn.microsoft.com/en-us/library/dd997402.aspx
  • @sming 关键是您想要同时处理(而不是阻塞 UI)而不是想要一个新线程。 ThreadPool 不会阻塞 UI 线程,但管理后台线程比通过创建线程手动执行的效率高得多。这就是 TPL 引入的思维过程的变化。不要考虑线程,要考虑并发任务。
  • @sming 对不起,那句话有点太粗俗了。任务的同步执行称为内联。当从 UI Thread 调度线程池(默认调度程序)上的任务时,它不会发生。仅当环境调度程序('TaskScheduler.Current')与您调用'.Wait()'的任务的调度程序相同时才会发生。由于 '.Wait()' 被阻塞,它无论如何都会阻塞 UI。简短:不要调用等待,它不会同步执行。
【解决方案2】:

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

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

请注意,在这两种情况下,您都可以通过方法组转换使代码稍微简单一些:

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

【讨论】:

  • +1。我想补充一点,与Task 相比,Thread 的级别非常低(我有一个详细说明的blog post)。我正在Grand Rapids DevDay 进行“在现实世界中使用任务”的演讲。该演讲被称为“线程已死”,因为不再需要 Thread(除非您正在实施 TaskScheduler)。
  • @StephenCleary,我假设你的意思是 Thread 已经死了,当它被用作后台线程时?
  • @ebb:不,我采取了我在第一条评论中描述的更强硬的立场。没有什么Thread 可以做(或BackgroundWorker)不能用Task 和适当的TaskScheduler 更优雅地完成。
  • @StephenCleary,如果不使用Thread,您将如何创建专用线程?
  • @ebb:我不清楚“专用线程”。如果您希望 Task 在特定线程上运行,请使用适当的 TaskScheduler - 例如,AsyncContextThread。但是,这通常不是必需的; SynchronizationContextThreadPoolConcurrentExclusiveSchedulerPair 调度程序对于大多数程序来说已经足够了。
【解决方案3】:

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

线程池的工作是共享和回收线程。它可以避免每次我们需要创建一个新线程时损失几毫秒。

进入线程池有几种方式:

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

【讨论】:

    【解决方案4】:

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

    您的第二个代码块执行相同的操作,但通过任务工厂实现中的 StartNew 方法委托(隐式移交)创建线程(后台 - 再次在线程池中运行)和启动线程的责任。

    这是给定代码块之间的快速区别。话虽如此,您可以谷歌搜索或查看我的其他贡献者的其他答案。

    【讨论】:

      猜你喜欢
      • 2021-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-13
      • 1970-01-01
      • 1970-01-01
      • 2020-12-21
      • 2014-08-06
      相关资源
      最近更新 更多