簡體   English   中英

使用任務(TPL)庫是否會使應用程序成為多線程?

[英]Does using Tasks (TPL) library make an application multithreaded?

最近在接受采訪時,我遇到了這個問題。

問:您是否編寫了多線程應用程序?

答:可以

問:想解釋更多嗎?

答:我使用Tasks (任務並行庫)執行一些任務,例如waiting for some info from internet while loading UI 這提高了我的應用程序的可用性。

問:但是,僅使用TPL意味着您編寫了multithreaded應用程序?

我:(不知道怎么說)

那么,到底什么是多線程應用程序? 與使用Tasks有區別嗎?

任務可以用來表示在多個線程上進行的操作,但不一定必須如此 可以編寫僅在單個線程中執行的復雜TPL應用程序。 例如,當您有一個任務代表某個數據的網絡請求時,該任務將不會創建其他線程來實現該目標。 這樣的程序(希望)是異步的,但不一定是多線程的。

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

讓我們在這里進行類比。


這是鮑勃做晚飯的方式:

  1. 他裝滿一鍋水,煮沸。
  2. 然后,他將面食放入水中。
  3. 完成后,他將面食瀝干。
  4. 他准備醬料。
  5. 他把所有調味料放在一個鍋里。
  6. 他煮醬汁。
  7. 他把醬汁放在意大利面上。
  8. 他吃晚餐。

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


這是簡做飯的方法:

  1. 她裝滿一鍋水,開始煮沸。
  2. 她准備醬料。
  3. 她把面食放在沸水中。
  4. 她把食材放在鍋里。
  5. 她排干意大利面。
  6. 她把醬汁放在意大利面上。
  7. 她吃晚餐。

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


以下是Servy烹飪晚餐的方式:

  1. 他告訴鮑勃燒開一鍋水,准備好后放入意大利面,然后將其送達意大利面。
  2. 他告訴簡為醬汁准備原料,將其煮熟,然后在面食上食用。
  3. 他等待鮑勃和簡完成。
  4. 他吃晚餐。

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.RunTask.Factory.Startnew開始一個新的任務,工作被排隊等待你在幕后ThreadPool ,其中TPL抽象了我們,讓您使用多個線程。

使用多個線程的常見情況是當您具有可以同時(並行)完成的CPU綁定工作時。 使用多線程應用程序負有重大責任。

因此,我們看到使用TPL並不一定意味着要使用多個線程,但是您絕對可以利用它來進行多線程處理。

問:但是,僅使用TPL就意味着您編寫了多線程應用程序?

聰明的問題,任務!=多線程,使用TaskCompletionSource可以創建一個可以在單線程(僅UI線程)中執行的Task

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

並且僅記住TPL知識就不能說您知道多線程。 您需要涵蓋許多概念。

  • 同步原語
  • 線程安全
  • 異步編程
  • 並行編程是TPL的一部分。
  • 還有更多...

當然還有Task並行庫。

注意:這不是完整列表,這些只是我的腦袋。


學習資源:

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

對於視頻教程,我建議使用Pluralsight 它已支付,但值得。

最后但並非最不重要的一點:當然可以,它是Stackoverflow

我的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, HttpClientStream.ReadAsync或使用線程池(包括IOCP池線程)的任何其他事物來引入並發。

形象地講,每個.NET應用程序都已經是多線程的,因為Framework廣泛使用ThreadPool 甚至一個簡單的控制台應用程序也將顯示System.Diagnostics.Process.GetCurrentProcess().Threads.Count多個線程。 您的面試官應該問過您是否編寫了並行 (或並行)代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM