首页 文章

任务与线程差异[重复]

提问于
浏览
260

这个问题在这里已有答案:

我是并行编程的新手 . .NET中有两个类: TaskThread .

所以,问题是:这些课程有什么区别?何时使用 ThreadTask 时更好?

4 回答

  • 28

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

    Task 不仅仅是"where to run some code"的抽象 - 它实际上只是"the promise of a result in the future" . 所以作为一些不同的例子:

    • Task.Delay 不是't need any actual CPU time; it'就像设置一个计时器以便将来关闭

    • WebClient.DownloadStringTaskAsync 返回的任务赢得了't take much CPU time locally; it',表示可能将大部分时间花在网络延迟或远程工作上的结果(在Web服务器上)

    • Task.Run() 返回的任务确实是在说"I want you to execute this code separately";该代码执行的确切线程取决于许多因素 .

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

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

  • 356

    Source

    Thread

    Thread表示一个实际的OS级线程,具有自己的堆栈和内核资源 . (从技术上讲,CLR实现可以使用光纤,但现有的CLR不会这样做)线程允许最高程度的控制;你可以Abort()或Suspend()或Resume()一个线程(虽然这是一个非常糟糕的主意),你可以观察它的状态,你可以设置线程级属性,如堆栈大小,公寓状态或文化 .

    Thread的问题在于OS线程成本高昂 . 您拥有的每个线程都会为其堆栈消耗大量内存,并在线程之间切换处理器上下文时增加额外的CPU开销 . 相反,最好让一小部分线程在工作可用时执行代码 .

    有些时候没有替代线程 . 如果需要指定名称(用于调试目的)或单元状态(用于显示UI),则必须创建自己的线程(请注意,具有多个UI线程通常是个坏主意) . 此外,如果您想维护一个由单个线程拥有并且只能由该线程使用的对象,那么为它显式创建一个Thread实例要容易得多,这样您就可以轻松检查尝试使用它的代码是否正在运行在正确的线程上 .

    ThreadPool

    ThreadPool是CLR维护的线程池的包装器 . ThreadPool完全没有控制权;您可以提交工作以在某个时刻执行,并且您可以控制池的大小,但是您不能设置其他任何内容 . 您甚至无法判断池何时开始运行您提交给它的工作 .

    使用ThreadPool可以避免创建太多线程的开销 . 但是,如果向线程池提交太多长时间运行的任务,它可能会变满,以后您提交的工作最终可能会等待较早的长时间运行的项目完成 . 此外,ThreadPool无法找出工作项何时完成(与Thread.Join()不同),也无法获得结果 . 因此,ThreadPool最适用于调用者不需要结果的短操作 .

    Task

    最后,任务并行库中的Task类提供了两全其美的功能 . 与ThreadPool一样,任务不会创建自己的OS线程 . 相反,任务由TaskScheduler执行;默认调度程序只在ThreadPool上运行 .

    与ThreadPool不同,Task还允许您查明它何时完成,并(通过通用任务)返回结果 . 您可以在现有任务上调用ContinueWith(),以便在任务完成后运行更多代码(如果它已经完成,它将立即运行回调) . 如果任务是通用的,ContinueWith()将向您传递任务的结果,允许您运行更多使用它的代码 .

    您还可以通过调用Wait()来同步等待任务完成(或者,对于通用任务,通过获取Result属性) . 与Thread.Join()一样,这将阻止调用线程,直到任务完成 . 同步等待任务通常是个坏主意;它可以防止调用线程执行任何其他工作,并且如果任务最终等待(甚至是异步)当前线程,也可能导致死锁 .

    以来任务仍然在ThreadPool上运行,它们不应该用于长时间运行的操作,因为它们仍然可以填满线程池并阻止新的工作 . 相反,Task提供了一个LongRunning选项,它将告诉TaskScheduler启动一个新线程而不是在ThreadPool上运行 .

    所有较新的高级并发API,包括Parallel.For *()方法,PLINQ,C#5等待,以及BCL中的现代异步方法都是基于Task构建的 .

    Conclusion

    最重要的是,任务几乎总是最好的选择;它提供了更强大的API,避免浪费操作系统线程 .

    在现代代码中显式创建自己的线程的唯一原因是设置每线程选项,或维护需要维护自己的标识的持久线程 .

  • 29

    通常你会听到Task是比线程更高级别的概念......这就是这句话的含义:

    • 你不能使用Abort / ThreadAbortedException,你应该在"business code"周期性地测试 token.IsCancellationRequested 标志中支持cancel事件(也避免长连接或无超时连接,例如db,否则你永远不会有机会测试这个标志) . 由类似的原因 Thread.Sleep(delay) 调用应该用 Task.Delay(delay, token) 调用替换 .

    • 任务中没有线程的 SuspendResume 方法功能 . Instance of task can't be reused 要么 .

    • 但是你得到两个新工具:

    a) continuations

    // 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) nested/child tasks

    //StartNew - starts task immediately, parent ends whith child
    var parent = Task.Factory.StartNew
    (() => {
              var child = Task.Factory.StartNew(() =>
             {
             //...
             });
          },  
          TaskCreationOptions.AttachedToParent
    );
    
    • 因此系统线程完全隐藏在任务之外,但仍然在具体的系统线程中执行任务的代码 . System threads are resources for tasks 当然还有任务并行执行的线程池 . 线程如何让新任务执行有不同的策略 . 另一个共享资源 TaskScheduler 关心它 . TaskScheduler 解决的一些问题1)更喜欢执行任务及其在同一线程中的最小化,从而最大限度地降低转换成本 - 也就是内联执行)2)更喜欢按照启动顺序执行任务 - 也就是PreferFairness 3)在非活动线程之间更有效地分配任务取决于"prior knowledge of tasks activity" - 又名工作窃取 . 重要提示:一般情况下"async"与"parallel"不同 . 使用TaskScheduler选项,您可以设置异步任务同步在一个线程中执行 . 要表达并行代码执行,可以使用更高的抽象(比任务):Parallel.ForEachPLINQDataflow .

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

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

  • 221

    Thread类用于在Windows中创建和操作thread .

    Task代表一些异步操作,是Task Parallel Library的一部分,_1855650是一组用于异步和并行运行任务的API .

    在旧的时代(即TPL之前)过去,使用 Thread 类是在后台或并行运行代码的标准方法之一(更好的选择通常是使用ThreadPool),但这很麻烦并且有几个缺点,其中最重要的是创建一个全新线程以在后台执行任务的性能开销 .

    如今,使用任务和TPL在90%的时间内都是一个更好的解决方案,因为它提供了抽象,可以更有效地使用系统资源 . 我想有一些场景你需要显式控制运行代码的线程,但一般来说,如果你想异步运行某些东西,你的第一个调用端口应该是TPL .

相关问题