[英]Does using Tasks (TPL) library make an application multithreaded?
最近在接受采訪時,我遇到了這個問題。
問:您是否編寫了多線程應用程序?
答:可以
問:想解釋更多嗎?
答:我使用Tasks
(任務並行庫)執行一些任務,例如waiting for some info from internet while loading UI
。 這提高了我的應用程序的可用性。
問:但是,僅使用TPL
意味着您編寫了multithreaded
應用程序?
我:(不知道怎么說)
那么,到底什么是多線程應用程序? 與使用Tasks
有區別嗎?
任務可以用來表示在多個線程上進行的操作,但不一定必須如此 。 可以編寫僅在單個線程中執行的復雜TPL應用程序。 例如,當您有一個任務代表某個數據的網絡請求時,該任務將不會創建其他線程來實現該目標。 這樣的程序(希望)是異步的,但不一定是多線程的。
並行性在同一時間做很多事情。 這可能是也可能不是多個線程的結果。
讓我們在這里進行類比。
這是鮑勃做晚飯的方式:
鮑勃在做飯時完全同步烹飪,沒有多線程,異步或並行性。
這是簡做飯的方法:
簡在烹飪晚餐時利用異步烹飪(沒有任何多線程)來實現並行性。
以下是Servy烹飪晚餐的方式:
Servy利用了多個線程(工作人員),每個線程各自獨立地同步完成工作,但彼此異步工作以實現並行性。
當然,如果我們考慮例如爐子有兩個燃燒器還是只有一個燃燒器,這將變得更加有趣。 如果我們的火爐有兩個燃燒器,那么我們的兩個線程,鮑勃和簡,就能在不互相干擾的情況下完成工作。 他們可能會碰到一點肩膀,或者每個人都時不時嘗試從同一個櫃子里拿東西,所以他們每個人都會放慢一點 ,但幅度不大。 如果他們每個人都需要共享一個爐灶,那么無論何時對方工作時,他們實際上根本無法完成很多工作。 在那種情況下,完成工作實際上不會比讓一個人完全同步地做飯快得多,就像鮑勃獨自一人做的那樣。 在這種情況下,我們正在使用多線程烹飪 , 但是我們的烹飪不是並行的 。 並非所有的多線程工作實際上都是並行工作 。 這是在具有一個CPU的計算機上運行多個線程時發生的情況。 實際上,您完成工作的速度不會比僅使用一個線程快,因為每個線程只是輪流進行工作。 (這並不意味着多線程程序在一個內核CPU上是沒有意義的,不是,不是,只是使用它們的原因並不是為了提高速度。)
我們甚至可以考慮使用任務並行庫來考慮這些廚師的工作方式,以了解TPL的用法與以下每種廚師相對應:
因此,首先我們有了bob,只需編寫普通的非TPL代碼並同步進行所有操作即可:
public class Bob : ICook
{
public IMeal Cook()
{
Pasta pasta = PastaCookingOperations.MakePasta();
Sauce sauce = PastaCookingOperations.MakeSauce();
return PastaCookingOperations.Combine(pasta, sauce);
}
}
然后,我們有了Jane,她啟動了兩個不同的異步操作,然后在啟動每個異步操作之后等待它們中的兩個以計算其結果。
public class Jane : ICook
{
public IMeal Cook()
{
Task<Pasta> pastaTask = PastaCookingOperations.MakePastaAsync();
Task<Sauce> sauceTask = PastaCookingOperations.MakeSauceAsync();
return PastaCookingOperations.Combine(pastaTask.Result, sauceTask.Result);
}
}
在此提醒一下,Jane正在使用TPL,並且她並行執行許多工作,但是她僅使用一個線程來完成工作。
然后,我們有Servy,他使用Task.Run
創建一個代表在另一個線程中進行工作的任務。 他啟動了兩個不同的工人,讓他們兩個都同時做一些工作,然后等待兩個工人完成。
public class Servy : ICook
{
public IMeal Cook()
{
var bobsWork = Task.Run(() => PastaCookingOperations.MakePasta());
var janesWork = Task.Run(() => PastaCookingOperations.MakeSauce());
return PastaCookingOperations.Combine(bobsWork.Result, janesWork.Result);
}
}
Task
是對未來工作的承諾。 當使用它,你可以使用它的I/O based
的工作,它不需要你使用多線程的代碼執行是。 一個很好的例子是將HttpClient
與C#5的async/await
結合HttpClient
,該功能可進行基於網絡的I / O工作。
但是,您可以利用TPL
進行多線程工作。 例如,當使用Task.Run
或Task.Factory.Startnew
開始一個新的任務,工作被排隊等待你在幕后ThreadPool
,其中TPL
抽象了我們,讓您使用多個線程。
使用多個線程的常見情況是當您具有可以同時(並行)完成的CPU綁定工作時。 使用多線程應用程序負有重大責任。
因此,我們看到使用TPL
並不一定意味着要使用多個線程,但是您絕對可以利用它來進行多線程處理。
問:但是,僅使用TPL就意味着您編寫了多線程應用程序?
聰明的問題,任務!=多線程,使用TaskCompletionSource可以創建一個可以在單線程(僅UI線程)中執行的Task
。
Task
只是對將來可能完成的操作的抽象。 這並不意味着代碼是多線程的。 通常, Task
涉及多線程,但不一定總是如此。
並且僅記住TPL
知識就不能說您知道多線程。 您需要涵蓋許多概念。
當然還有Task並行庫。
注意:這不是完整列表,這些只是我的腦袋。
對於單獨的線程,我建議http://www.albahari.com/threading/
對於視頻教程,我建議使用Pluralsight 。 它已支付,但值得。
最后但並非最不重要的一點:當然可以,它是Stackoverflow 。
我的5分錢:您不必顯式地將線程與Task.Run
或Task.Factory.StartNew
即可使您的TPL應用程序成為多線程。 考慮一下:
async static Task TestAsync()
{
Func<Task> doAsync = async () =>
{
await Task.Delay(1).ConfigureAwait(false);
Console.WriteLine(new { Thread.CurrentThread.ManagedThreadId });
};
var tasks = Enumerable.Range(0, 10).Select(i => doAsync());
await Task.WhenAll(tasks);
}
// ...
TestAsync().Wait();
后該代碼await
內doAsync
是在不同的線程同時執行。 以類似的方式,可以通過異步套接字API, HttpClient
, Stream.ReadAsync
或使用線程池(包括IOCP池線程)的任何其他事物來引入並發。
形象地講,每個.NET應用程序都已經是多線程的,因為Framework廣泛使用ThreadPool
。 甚至一個簡單的控制台應用程序也將顯示System.Diagnostics.Process.GetCurrentProcess().Threads.Count
多個線程。 您的面試官應該問過您是否編寫了並行 (或並行)代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.