409

I'm new to parallel programming. There are two classes available in .NET: Task and Thread.

So, my questions are:

  • What is the difference between those classes?
  • When is it better to use Thread over Task (and vice-versa)?
4

3 回答 3

502

Thread是一个较低级别的概念:如果您直接启动一个线程,您知道它将是一个单独的线程,而不是在线程池等上执行。

Task不仅仅是“在哪里运行一些代码”的抽象——它实际上只是“对未来结果的承诺”。所以作为一些不同的例子:

  • Task.Delay不需要任何实际的 CPU 时间;这就像设置一个计时器在未来关闭
  • 返回的任务WebClient.DownloadStringTaskAsync在本地不会占用太多 CPU 时间;它代表的结果可能大部分时间都花在网络延迟或远程工作(在 Web 服务器上)
  • Task.Run()真正返回的任务说“我希望你单独执行这段代码”;该代码执行的确切线程取决于许多因素。

请注意,Task<T>抽象对于 C# 5 中的异步支持至关重要。

一般来说,我建议您尽可能使用更高级别的抽象:在现代 C# 代码中,您应该很少需要显式启动自己的线程。

于 2012-11-17T09:03:57.337 回答
49

通常你会听到Task 是一个比线程更高级别的概念......这就是这个短语的意思:

  1. 你不能使用 Abort/ThreadAbortedException,你应该在你的“业务代码”中支持取消事件定期测试token.IsCancellationRequested标志(也避免长时间或无超时的连接,例如到 db,否则你将永远没有机会测试这个标志)。出于类似的原因Thread.Sleep(delay),应该将调用替换为 Task.Delay(delay, token)调用(在内部传递令牌以有可能中断延迟)。

  2. 任务没有线程SuspendResume方法功能。任务实例也不能重用

  3. 但是你会得到两个新工具:

    a)延续

    // continuation with ContinueWhenAll - execute the delegate, when ALL
    // tasks[] had been finished; other option is ContinueWhenAny
    
    Task.Factory.ContinueWhenAll( 
       tasks,
       () => {
           int answer = tasks[0].Result + tasks[1].Result;
           Console.WriteLine("The answer is {0}", answer);
       }
    );
    

    b)嵌套/子任务

    //StartNew - starts task immediately, parent ends whith child
    var parent = Task.Factory.StartNew
    (() => {
              var child = Task.Factory.StartNew(() =>
             {
             //...
             });
          },  
          TaskCreationOptions.AttachedToParent
    );
    
  4. 因此系统线程对任务完全隐藏,但任务的代码仍然在具体的系统线程中执行。系统线程是任务的资源,当然在任务的并行执行下仍然存在线程池。线程如何执行新任务可以有不同的策略。另一个共享资源TaskScheduler关心它。TaskScheduler解决的一些问题1) 更喜欢在同一个线程中执行任务及其延续,最小化切换成本 - 也就是内联执行) 2) 更喜欢按照任务开始的顺序执行任务 - 也就是PreferFairness 3) 在非活动线程之间更有效地分配任务取决于“任务活动的先验知识” - 又名偷工减料。重要提示:通常“异步”与“并行”不同。使用 TaskScheduler 选项,您可以设置异步任务在一个线程中同步执行。为了表达并行代码执行,可以使用更高的抽象(比任务):Parallel.ForEach, PLINQ, Dataflow.

  5. 任务与 C# async/await 功能(即Promise Model )集成,例如requestButton.Clicked += async (o, e) => ProcessResponce(await client.RequestAsync(e.ResourceName));,执行client.RequestAsync不会阻塞 UI 线程。重要提示:幕后Clicked委托调用是绝对常规的(所有线程都由编译器完成)。

这足以做出选择。如果您需要支持取消调用倾向于挂起的遗留 API 的功能(例如无超时连接)并且在这种情况下支持 Thread.Abort(),或者如果您正在创建多线程后台计算并希望使用暂停/恢复优化线程之间的切换,这意味着手动管理并行执行 - 留在线程。否则请转到任务,因为它们可以让您轻松操作它们的组,集成到语言中并提高开发人员的工作效率 -任务并行库 (TPL)

于 2013-02-13T12:30:10.037 回答
40

The Thread class is used for creating and manipulating a thread in Windows.

A Task represents some asynchronous operation and is part of the Task Parallel Library, a set of APIs for running tasks asynchronously and in parallel.

In the days of old (i.e. before TPL) it used to be that using the Thread class was one of the standard ways to run code in the background or in parallel (a better alternative was often to use a ThreadPool), however this was cumbersome and had several disadvantages, not least of which was the performance overhead of creating a whole new thread to perform a task in the background.

Nowadays using tasks and the TPL is a far better solution 90% of the time as it provides abstractions which allows far more efficient use of system resources. I imagine there are a few scenarios where you want explicit control over the thread on which you are running your code, however generally speaking if you want to run something asynchronously your first port of call should be the TPL.

于 2014-03-27T19:12:45.307 回答