一尘不染

使用任务(TPL)库是否会使应用程序成为多线程?

c#

最近在接受采访时,我遇到了这个问题。

问:您是否编写了多线程应用程序?

答:可以

问:想解释更多吗?

答:我使用Tasks(任务并行库)来执行某些任务,例如waiting for some info from internet while loading UI。这提高了我的应用程序的可用性。

问:但是,您使用过TPL意味着您已经编写了multithreaded应用程序?

我:(不知道怎么说)

那么,到底什么是多线程应用程序?与使用不同Tasks吗?


阅读 226

收藏
2020-05-19

共1个答案

一尘不染

任务 可以 用来表示在多个线程上进行的操作,但不一定 必须如此 。一个 可以
写一个永远只能执行单一线程的复杂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);
    }
}
2020-05-19