最近在接受采访时,我遇到了这个问题。
问:您是否编写了多线程应用程序?
答:可以
问:想解释更多吗?
答:我使用Tasks(任务并行库)来执行某些任务,例如waiting for some info from internet while loading UI。这提高了我的应用程序的可用性。
Tasks
waiting for some info from internet while loading UI
问:但是,您使用过TPL意味着您已经编写了multithreaded应用程序?
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用来创建一个任务,该任务 表示在另一个线程中进行工作 。他启动了两个不同的工人,让他们两个都同时做一些工作,然后等待两个工人完成。
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); } }