本文翻译自:Task vs Thread differences [duplicate]
This question already has an answer here: 这个问题已经在这里有了答案:
- What is the difference between task and thread? 任务和线程有什么区别? 8 answers 8个答案
I'm new to parallel programming. 我是并行编程的新手。 There are two classes available in .NET: Task
and Thread
. .NET中有两个可用的类: Task
和Thread
。
So, my questions are: 因此,我的问题是:
- What is difference between those classes? 这些类之间有什么区别?
- When is it better to use
Thread
and whenTask
? 什么时候使用Thread
更好,何时使用Task
?
#1楼
参考:https://stackoom.com/question/uLWr/任务与线程的差异-重复
#2楼
Thread
is a lower-level concept: if you're directly starting a thread, you know it will be a separate thread, rather than executing on the thread pool etc. Thread
是一个较低层的概念:如果您直接启动一个线程,则知道它将是一个单独的线程,而不是在线程池等上执行。
Task
is more than just an abstraction of "where to run some code" though - it's really just "the promise of a result in the future". Task
不仅仅是对“在哪里运行某些代码”的抽象-它实际上只是“对未来结果的承诺”。 So as some different examples: 因此,作为一些不同的示例:
-
Task.Delay
doesn't need any actual CPU time;Task.Delay
不需要任何实际的CPU时间; it's just like setting a timer to go off in the future 就像将计时器设置为将来关闭 - A task returned by
WebClient.DownloadStringTaskAsync
won't take much CPU time locally;WebClient.DownloadStringTaskAsync
返回的任务在本地不会花费很多CPU时间; it's representing a result which is likely to spend most of its time in network latency or remote work (at the web server) 它代表的结果可能是将其大部分时间花费在网络延迟或远程工作(在Web服务器上)上 - A task returned by
Task.Run()
really is saying "I want you to execute this code separately";Task.Run()
返回的任务实际上是在说“我希望您分别执行此代码”; the exact thread on which that code executes depends on a number of factors. 该代码执行的确切线程取决于许多因素。
Note that the Task<T>
abstraction is pivotal to the async support in C# 5. 请注意, Task<T>
抽象对于C#5中的异步支持至关重要。
In general, I'd recommend that you use the higher level abstraction wherever you can: in modern C# code you should rarely need to explicitly start your own thread. 通常,我建议您尽可能使用更高级别的抽象:在现代C#代码中,您几乎不需要显式启动自己的线程。
#3楼
Usually you hear Task is a higher level concept than thread ... and that's what this phrase means: 通常您会听到“ 任务”是一个比线程更高级的概念 ……这就是这个短语的意思:
You can't use Abort/ThreadAbortedException, you should support cancel event in your "business code" periodically testing
token.IsCancellationRequested
flag (also avoid long or timeoutless connections eg to db, otherwise you will never get a chance to test this flag). 您不能使用Abort / ThreadAbortedException,您应该在定期测试token.IsCancellationRequested
标志的“业务代码”中支持cancel事件(还应避免长时间或超时的连接,例如与db的连接,否则您将永远没有机会测试此标志)。 By the similar reasonThread.Sleep(delay)
call should be replaced withTask.Delay(delay, token)
call (passing token inside to have possibility to interrupt delay).Task.Delay(delay, token)
类似的原因,Thread.Sleep(delay)
调用应替换为Task.Delay(delay, token)
调用(将令牌传递到内部可以中断延迟)。There are no thread's
Suspend
andResume
methods functionality with tasks. 任务没有线程的Suspend
和Resume
方法功能。 Instance of task can't be reused either. 任务实例也不能重用 。But you get two new tools: 但是您会获得两个新工具:
a) continuations 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) nested/child tasks b) 嵌套/子任务
//StartNew - starts task immediately, parent ends whith child var parent = Task.Factory.StartNew (() => { var child = Task.Factory.StartNew(() => { //... }); }, TaskCreationOptions.AttachedToParent );
So system thread is completely hidden from task, but still task's code is executed in the concrete system thread. 因此,系统线程对任务完全隐藏了,但是任务的代码仍然在具体的系统线程中执行。 System threads are resources for tasks and ofcourse there is still thread pool under the hood of task's parallel execution. 系统线程是任务的资源,当然 ,在任务的并行执行机制下仍然存在线程池。 There can be different strategies how thread get new tasks to execute. 线程如何获得执行新任务的策略可能不同。 Another shared resource TaskScheduler cares about it. 另一个共享资源TaskScheduler关心它。 Some problems that TaskScheduler solves 1) prefer to execute task and its conitnuation in the same thread minimizing switching cost - aka inline execution ) 2) prefer execute tasks in an order they were started - aka PreferFairness 3) more effective distribution of tasks between inactive threads depending on "prior knowledge of tasks activity" - aka Work Stealing . TaskScheduler解决的一些问题1)倾向于在同一线程中执行任务及其连接,以最大程度地降低切换成本-aka 内联执行 )2)倾向于按启动顺序执行任务-aka优先 3)在非活动线程之间更有效地分配任务取决于“任务活动的先验知识”-又名“ 工作偷窃” 。 Important: in general "async" is not same as "parallel". 重要说明:通常,“异步”与“并行”不同。 Playing with TaskScheduler options you can setup async tasks be executed in one thread synchronously. 使用TaskScheduler选项,您可以设置异步任务在一个线程中同步执行。 To express parallel code execution higher abstractions (than Tasks) could be used:
Parallel.ForEach
,PLINQ
,Dataflow
. 为了表示并行代码执行,可以使用更高的抽象度(比Tasks高):Parallel.ForEach
,PLINQ
和Dataflow
。Tasks are integrated with C# async/await features aka Promise Model , eg there
requestButton.Clicked += async (o, e) => ProcessResponce(await client.RequestAsync(e.ResourceName));
任务与C#异步/等待功能(即Promise Model)集成在一起,例如requestButton.Clicked += async (o, e) => ProcessResponce(await client.RequestAsync(e.ResourceName));
the execution ofclient.RequestAsync
will not block UI thread.client.RequestAsync
的执行不会阻止UI线程。 Important: under the hoodClicked
delegate call is absolutely regular (all threading is done by compiler). 重要提示:Clicked
委托调用绝对是常规的(所有线程均由编译器完成)。
That is enough to make a choice. 这足以做出选择。 If you need to support Cancel functionality of calling legacy API that tends to hang (eg timeoutless connection) and for this case supports Thread.Abort(), or if you are creating multithread background calculations and want to optimize switching between threads using Suspend/Resume, that means to manage parallel execution manually - stay with Thread. 如果您需要支持取消倾向于挂起的旧API的取消功能(例如,超时连接),并且在这种情况下支持Thread.Abort(),或者您正在创建多线程后台计算,并且想要使用挂起/恢复来优化线程之间的切换,这意味着要手动管理并行执行-继续使用Thread。 Otherwise go to Tasks because of they will give you easy manipulate on groups of them, are integrated into the language and make developers more productive - Task Parallel Library (TPL) . 否则,请访问Tasks,因为它们将使您能够轻松地对它们进行分组,将其集成到语言中并提高开发人员的工作效率-Task Parallel Library(TPL) 。
#4楼
The Thread
class is used for creating and manipulating a thread in Windows. Thread
类用于在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. Task
代表某种异步操作,并且是任务并行库的一部分, 任务并行库是一组用于异步和并行运行任务的API。
In the days of old (ie 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. 在过去(即在TPL之前),过去曾经使用Thread
类是在后台或并行运行代码的标准方法之一(更好的选择通常是使用ThreadPool
),但这很麻烦并有几个缺点,其中最主要的缺点是创建一个新线程在后台执行任务的性能开销。
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. 如今,使用任务和TPL是90%时更好的解决方案,因为它提供了抽象,从而可以更有效地利用系统资源。 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. 我想象有几种情况需要对运行代码的线程进行显式控制,但是通常来说,如果要异步运行某些东西,则第一个调用端口应该是TPL。
#5楼
Thread 线
Thread represents an actual OS-level thread, with its own stack and kernel resources. 线程代表一个实际的OS级线程,具有自己的堆栈和内核资源。 (technically, a CLR implementation could use fibers instead, but no existing CLR does this) Thread allows the highest degree of control; (从技术上讲,CLR实现可以使用光纤代替,但是现有的CLR不能这样做)线程允许最高程度的控制; you can Abort() or Suspend() or Resume() a thread (though this is a very bad idea), you can observe its state, and you can set thread-level properties like the stack size, apartment state, or culture. 您可以中断一个线程的Abort()或Suspend()或Resume()(尽管这是一个非常糟糕的主意),可以观察其状态,还可以设置线程级属性,例如堆栈大小,单元状态或区域性。
The problem with Thread is that OS threads are costly. Thread的问题在于OS线程的成本很高。 Each thread you have consumes a non-trivial amount of memory for its stack, and adds additional CPU overhead as the processor context-switch between threads. 您拥有的每个线程在其堆栈上消耗的内存并不少,并且随着处理器在线程之间的上下文切换而增加了额外的CPU开销。 Instead, it is better to have a small pool of threads execute your code as work becomes available. 相反,最好在工作可用时让一小部分线程执行代码。
There are times when there is no alternative Thread. 有时没有替代线程。 If you need to specify the name (for debugging purposes) or the apartment state (to show a UI), you must create your own Thread (note that having multiple UI threads is generally a bad idea). 如果需要指定名称(出于调试目的)或单元状态(以显示UI),则必须创建自己的线程(注意,拥有多个UI线程通常是个坏主意)。 Also, if you want to maintain an object that is owned by a single thread and can only be used by that thread, it is much easier to explicitly create a Thread instance for it so you can easily check whether code trying to use it is running on the correct thread. 另外,如果您要维护一个对象,该对象由单个线程拥有并且只能由该线程使用,则为该对象显式创建一个Thread实例要容易得多,因此您可以轻松地检查尝试使用该对象的代码是否正在运行在正确的线程上。
ThreadPool 线程池
ThreadPool is a wrapper around a pool of threads maintained by the CLR. ThreadPool是由CLR维护的线程池的包装。 ThreadPool gives you no control at all; ThreadPool完全不提供任何控制。 you can submit work to execute at some point, and you can control the size of the pool, but you can't set anything else. 您可以提交要在某个时间执行的工作,还可以控制池的大小,但不能设置其他任何内容。 You can't even tell when the pool will start running the work you submit to it. 您甚至无法确定池何时开始运行您提交给池的工作。
Using ThreadPool avoids the overhead of creating too many threads. 使用ThreadPool避免了创建过多线程的开销。 However, if you submit too many long-running tasks to the threadpool, it can get full, and later work that you submit can end up waiting for the earlier long-running items to finish. 但是,如果您向线程池提交了太多长时间运行的任务,则该任务可能已满,并且您提交的以后的工作可能最终会等待较早的长时间运行的项目完成。 In addition, the ThreadPool offers no way to find out when a work item has been completed (unlike Thread.Join()), nor a way to get the result. 另外,ThreadPool没有提供任何方法来确定工作项何时完成(不同于Thread.Join()),也没有获得结果的方法。 Therefore, ThreadPool is best used for short operations where the caller does not need the result. 因此,ThreadPool最好用于调用者不需要结果的简短操作。
Task 任务
Finally, the Task class from the Task Parallel Library offers the best of both worlds. 最后,Task Parallel Library中的Task类提供了两全其美的方法。 Like the ThreadPool, a task does not create its own OS thread. 像ThreadPool一样,任务不会创建自己的OS线程。 Instead, tasks are executed by a TaskScheduler; 而是由TaskScheduler执行任务。 the default scheduler simply runs on the ThreadPool. 默认调度程序仅在ThreadPool上运行。
Unlike the ThreadPool, Task also allows you to find out when it finishes, and (via the generic Task) to return a result. 与ThreadPool不同,Task还允许您找出何时完成,并(通过通用Task)返回结果。 You can call ContinueWith() on an existing Task to make it run more code once the task finishes (if it's already finished, it will run the callback immediately). 您可以在现有Task上调用ContinueWith(),以使其在任务完成后运行更多代码(如果已完成,它将立即运行回调)。 If the task is generic, ContinueWith() will pass you the task's result, allowing you to run more code that uses it. 如果该任务是通用任务,则ContinueWith()将为您传递任务的结果,从而允许您运行更多使用该任务的代码。
You can also synchronously wait for a task to finish by calling Wait() (or, for a generic task, by getting the Result property). 您还可以通过调用Wait()来同步等待任务完成(或者,对于通用任务,可以通过获取Result属性来同步等待任务完成)。 Like Thread.Join(), this will block the calling thread until the task finishes. 像Thread.Join()一样,这将阻塞调用线程,直到任务完成。 Synchronously waiting for a task is usually bad idea; 同步等待任务通常不是一个好主意。 it prevents the calling thread from doing any other work, and can also lead to deadlocks if the task ends up waiting (even asynchronously) for the current thread. 它阻止了调用线程执行任何其他工作,并且如果任务最终(甚至异步地)等待当前线程,也可能导致死锁。
Since tasks still run on the ThreadPool, they should not be used for long-running operations, since they can still fill up the thread pool and block new work. 由于任务仍在ThreadPool上运行,因此不应将它们用于长时间运行的操作,因为它们仍然可以填满线程池并阻止新工作。 Instead, Task provides a LongRunning option, which will tell the TaskScheduler to spin up a new thread rather than running on the ThreadPool. 相反,Task提供了LongRunning选项,该选项将告诉TaskScheduler启动新线程,而不是在ThreadPool上运行。
All newer high-level concurrency APIs, including the Parallel.For*() methods, PLINQ, C# 5 await, and modern async methods in the BCL, are all built on Task. 所有较新的高级并发API(包括Parallel.For *()方法,PLINQ,C#5等待和BCL中的现代异步方法)都基于Task构建。
Conclusion 结论
The bottom line is that Task is almost always the best option; 最重要的是,Task几乎总是最佳选择。 it provides a much more powerful API and avoids wasting OS threads. 它提供了更强大的API,并避免浪费操作系统线程。
The only reasons to explicitly create your own Threads in modern code are setting per-thread options, or maintaining a persistent thread that needs to maintain its own identity. 在现代代码中显式创建自己的线程的唯一原因是设置每个线程选项,或维护需要维护自己身份的持久线程。