19

最近在接受採訪時,我得到了這個問題。使用任務(TPL)庫是否使應用程序多線程?

問:你寫過多線程應用程序嗎?

答:是

問:請多解釋一下?答:我用Tasks(任務並行庫)來執行一些任務,如waiting for some info from internet while loading UI。這提高了我的應用程序可用性。

問:但是,您剛纔使用的TPL意味着您已經編寫了multithreaded應用程序?

我:(不知道到什麼say1)

那麼,究竟什麼多線程應用程序?它與使用Tasks有什麼不同?

回答

77

任務可以用於表示發生在多個線程上的操作,但它們不具有具有。一個可以編寫只在單個線程中執行的複雜TPL應用程序。例如,當您有一項任務代表某些數據的網絡請求時,該任務是而不是將創建其他線程來實現該目標。這樣的程序(希望)是異步的,但不一定是多線程的。

並行性在同一時間做了不止一件事情。這可能是也可能不是多個線程的結果。

讓我們在這裏打個比方。


這裏是鮑勃怎麼做晚飯:

  1. 他填補了一鍋水,並煮沸它。
  2. 然後他把意大利麪放在水中。
  3. 完成後,他將意大利麪排出。
  4. 他準備他的醬料。
  5. 他把所有醬料放在平底鍋裏。
  6. 他煮他的醬。
  7. 他把自己的醬放在他的意大利麪上。
  8. 他吃晚飯。

鮑勃烹飪完晚餐時完全同步烹飪,沒有多線程,異步或並行性。


這裏是簡如何做晚飯:

  1. 她充滿了一盆水,並開始沸騰了。
  2. 她爲她的醬汁準備食材。
  3. 她把麪條在開水。
  4. 她把配料放在平底鍋裏。
  5. 她喝了她的麪食。
  6. 她把醬油在她的麪食。
  7. 她吃了她的晚餐。

珍在烹飪晚餐時利用異步烹飪(沒有多線程)實現並行性。


這裏是Servy如何做晚飯:

  1. 他告訴鮑勃燒開一鍋水,放入麪條準備好時,全心全意麪食。
  2. 他告訴簡準備配料醬,煮它,然後做的時候爲它服務過的麪食。
  3. 他等待Bob和Jane完成。
  4. 他吃他的晚餐。

Servy利用多個線程(工作人員),他們每個人都同步工作,但彼此異步工作以實現並行性。

當然,這一切變得更有趣,如果我們考慮,例如,我們的爐子是否有兩個燃燒器或只是一個。如果我們的爐子有兩個燃燒器,那麼我們的兩個線程Bob和Jane都能夠完成他們的工作,而不會相互影響。他們可能會碰到肩膀位,或每個嘗試時不時搶在同一個機櫃的東西,所以他們會每放慢一個,但數量不多。如果他們每個人都需要共用一臺爐竈,那麼只要對方正在工作,他們實際上也不會完成任何工作。在這種情況下,工作實際上不會比只讓一個人完全同步做飯更快,就像鮑勃自己做飯時一樣。在這種情況下,我們用多線程烹飪,,但我們的烹飪不是並行化的並非所有的多線程工作實際上是並行工作。這是當你在一臺CPU上運行多個線程時發生的情況。你並沒有比使用一個線程更快地完成工作,因爲每個線程只是輪流工作。 (這並不意味着多線程程序在一個內核CPU上毫無意義,它們不是,只是使用它們的原因不是爲了提高速度。)


我們甚至可以考慮如何將這些廚師將使用任務並行庫,看TPL什麼用做他們的工作對應於每種類型的廚師的:

所以首先我們有鮑勃· ,只是寫正常的非TPL代碼,並同步做的一切:

public class Bob : ICook 
{ 
    public IMeal Cook() 
    { 
     Pasta pasta = PastaCookingOperations.MakePasta(); 
     Sauce sauce = PastaCookingOperations.MakeSauce(); 
     return PastaCookingOperations.Combine(pasta, sauce); 
    } 
} 

然後我們再簡,誰啓動兩個不同的異步操作,從他們每個人的計算結果她之後等待他們兩個。

public class Jane : ICook 
{ 
    public IMeal Cook() 
    { 
     Task<Pasta> pastaTask = PastaCookingOperations.MakePastaAsync(); 
     Task<Sauce> sauceTask = PastaCookingOperations.MakeSauceAsync(); 
     return PastaCookingOperations.Combine(pastaTask.Result, sauceTask.Result); 
    } 
} 

這裏作爲提醒,簡正在使用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); 
    } 
} 
+7

只是'真棒'。偉大的比喻。謝謝:) –

+3

+1 Servy的晚餐很好吃,讓我們來看看[Eric Lippert的早餐](http://msdn.microsoft.com/en-us/magazine/hh456401.aspx):) –

+3

寫得很好!燃燒器的比喻很棒。 –

2

A Task是未來工作的承諾完成。當使用它時,你可以使用它作爲I/O based工作,其中不需要要求你使用多個線程來執行代碼。一個很好的例子就是使用C#5的async/awaitHttpClient這個功能來完成基於網絡的I/O工作。

但是,您可以利用TPL來執行多線程工作。例如,當使用Task.RunTask.Factory.Startnew開始一項新任務時,幕後工作在ThreadPool上排隊等待,TPL爲我們提取,允許您使用多個線程。

使用多線程的一種常見情況是當您有可以同時並行完成的CPU綁定工作時。使用多線程應用程序有很大的責任。

所以我們看到與TPL一起工作並不意味着使用多線程,但您絕對可以利用它來執行多線程。

2

問:但是,您剛剛使用過TPL意味着您已經編寫了一個 多線程應用程序?

聰明的問題,任務!=多線程,使用TaskCompletionSource您可以創建一個Task可以在單個線程執行(可能是UI線程只)本身。

Task只是對未來可能完成的操作的抽象。這並不意味着代碼是多線程的。通常Task涉及多線程,不一定總是。

請記住,只有知道TPL你不能說你知道多線程。你需要涵蓋許多概念。

  • 螺紋
  • 同步原語
  • 線程安全
  • 異步編程
  • 並行編程是TPL的一部分。
  • 和很多更多...

當然任務並行庫太的和。

注意:這不是完整列表,這些只是從我的頭頂。


參考資料瞭解:

對於單獨穿,我建議http://www.albahari.com/threading/

對於視頻教程我建議Pluralsight。它是支付的,但值得的成本。

最後但並非最不重要:當然是,它是Stackoverflow

+0

@nowhewhomustnotbenamed,檢查斯蒂芬克萊裏的博客(和他的書也):http://blog.stephencleary.com。此外,['task-parallel-library'標籤wiki](http://stackoverflow.com/tags/task-parallel-library/info)列出了一些免費的電子書。 – Noseratio

+0

@sriram Sakthivel:謝謝你一個Sriram。非常有趣的資源。再次感謝:) –

1

我5毛錢:你不必與Task.RunTask.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(); 

awaitdoAsync是在不同的線程同時執行後的代碼。以類似的方式,可以使用異步套接字API HttpClient,Stream.ReadAsync或使用線程池(包括IOCP池線程)的其他任何東西引入併發。

形象地說,每個.NET應用程序都是多線程的,因爲Framework廣泛使用ThreadPool。即使是一個簡單的控制檯應用程序也會顯示多個線程System.Diagnostics.Process.GetCurrentProcess().Threads.Count。你的面試官應該問你是否寫了併發(或並行)的代碼。

相關問題