任務與線程的差異[重複]

本文翻譯自:Task vs Thread differences [duplicate]

This question already has an answer here: 這個問題已經在這裏有了答案:

I'm new to parallel programming. 我是並行編程的新手。 There are two classes available in .NET: Task and Thread . .NET中有兩個可用的類: TaskThread

So, my questions are: 因此,我的問題是:

  • What is difference between those classes? 這些類之間有什麼區別?
  • When is it better to use Thread and when Task ? 什麼時候使用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: 通常您會聽到“ 任務”是一個比線程更高級的概念 ……這就是這個短語的意思:

  1. 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 reason Thread.Sleep(delay) call should be replaced with Task.Delay(delay, token) call (passing token inside to have possibility to interrupt delay). Task.Delay(delay, token)類似的原因, Thread.Sleep(delay)調用應替換爲Task.Delay(delay, token)調用(將令牌傳遞到內部可以中斷延遲)。

  2. There are no thread's Suspend and Resume methods functionality with tasks. 任務沒有線程的SuspendResume方法功能。 Instance of task can't be reused either. 任務實例也不能重用

  3. 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 ); 
  4. 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.ForEachPLINQDataflow

  5. 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 of client.RequestAsync will not block UI thread. client.RequestAsync的執行不會阻止UI線程。 Important: under the hood Clicked 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樓

Source 資源

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. 在現代代碼中顯式創建自己的線程的唯一原因是設置每個線程選項,或維護需要維護自己身份的持久線程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章