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。你的面试官应该问你是否写了并发(或并行)的代码。

相关问题