是否使用任务(TPL)库使应用程序multithreading?
最近在接受采访时,我得到了这个问题。
问:你写了multithreading应用程序吗?
答:是的
问:要多解释一下?
答:我使用Tasks
(任务并行库)来执行一些任务,如waiting for some info from internet while loading UI
。 这提高了我的应用程序可用性。
问:但是,您刚刚使用过TPL
意味着您已经编写了multithreaded
应用程序?
我:(不知道该说什么1)
那么,什么是一个multithreading应用程序? 与使用Tasks
不同吗?
任务可以用来表示在多个线程上进行的操作,但是他们不需要。 可以编写复杂的TPL应用程序,这些应用程序只能在单个线程中执行。 当你有一个任务,例如,代表一些数据的networking请求,这个任务不会创build额外的线程来实现这个目标。 这样的程序(希望)是asynchronous的,但不一定是multithreading的。
并行性在同一时间做了不止一件事情。 这可能是也可能不是多个线程的结果。
让我们在这里进行比喻。
下面是鲍勃如何烹饪晚餐:
- 他把一壶水烧开了。
- 然后他把意大利面放在水里。
- 当它完成时,他把意大利面排干了。
- 他准备他的酱料。
- 他把所有的酱料都放在平底锅里。
- 他煮他的酱汁。
- 他把他的酱放在他的意大利面上。
- 他吃晚饭。
鲍勃煮完晚餐时,完全同步烹饪,没有multithreading,asynchronous或并行性。
简是这样做的晚餐:
- 她装满了一壶水,然后开始煮。
- 她准备调味料。
- 她把面食放在沸水里。
- 她把配料放在平底锅里
- 她把意大利面倒掉了
- 她把酱放在她的意大利面上。
- 她吃晚餐。
Jane在烹调晚餐时利用asynchronous烹饪(没有任何multithreading)来实现并行性。
下面是Servy如何做饭的晚餐:
- 他告诉鲍勃烧开一壶水,准备好时放进面食,然后服务面食。
- 他告诉简,准备调味酱的原料,煮熟,然后在做完意大利面后供应。
- 他等着Bob和Jane完成。
- 他吃他的晚餐。
Servy利用多个线程(工作人员),每个线程(工作人员)各自同步地工作,但彼此asynchronous工作以实现并行性。
当然,如果我们考虑,例如,我们的炉子是两个燃烧器还是只有一个,这就更加有趣了。 如果我们的炉子有两个燃烧器,那么我们的两个线程,Bob和Jane,都能够做到他们的工作,而不是彼此相处。 他们可能会碰一下肩膀,或者每次都试图从同一个橱柜里拿东西,所以他们每个人都会放慢一点 ,但是不会太多。 如果他们每个人都需要分享一个炉子的燃烧器,那么只要对方正在工作,他们实际上也不会做太多的工作。 在这种情况下,工作实际上不会比只让一个人完全同步做饭更快,就像鲍勃自己做饭一样。 在这种情况下,我们用multithreading烹饪 , 但是我们的烹饪不是并行的 。 并非所有的multithreading工作实际上是并行工作 。 在一台CPU上运行多个线程时会发生这种情况。 你实际上并没有比使用一个线程更快地完成工作,因为每个线程只是轮stream工作。 (这并不意味着multithreading程序在一个内核CPU上毫无意义,它们不是,只是使用它们的原因不是为了提高速度。)
我们甚至可以考虑这些厨师如何使用任务并行库来完成他们的工作,以查看TPL对应于这些types的厨师的用途:
所以首先我们有bob,只需编写普通的非TPL代码,
public class Bob : ICook { public IMeal Cook() { Pasta pasta = PastaCookingOperations.MakePasta(); Sauce sauce = PastaCookingOperations.MakeSauce(); return PastaCookingOperations.Combine(pasta, sauce); } }
然后,我们让Jane开始了两个不同的asynchronous操作,然后在启动每个asynchronous操作之后等待他们两个来计算她的结果。
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
来创build一个代表在另一个线程中工作的任务。 他启动了两个不同的工作人员,他们各自同时做一些工作,然后等待两个工人完成。
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
工作, 而不需要使用多个线程来执行代码。 一个很好的例子就是使用C#5的async/await
function来处理基于networking的I / O工作的HttpClient
。
但是,您可以利用TPL
来执行multithreading工作。 例如,当使用Task.Run
或Task.Factory.Startnew
来启动一个新的任务时,幕后的工作就会在ThreadPool
上排队等候, TPL
为我们提供了抽象,允许你使用多个线程。
使用multithreading的一个常见情况是当你有CPU绑定的工作,可以同时完成(并行)。 使用multithreading应用程序有很大的责任。
所以我们看到和TPL
一起工作并不意味着使用multithreading,但是你一定可以利用它来做multithreading。
问:但是,您刚刚使用过TPL意味着您已经编写了multithreading应用程序?
聪明的问题,任务!=multithreading,使用TaskCompletionSource你可以创build一个Task
,可以在单线程(可能只是UI线程)本身执行。
Task
只是一个对将来可能完成的操作的抽象。 这并不意味着代码是multithreading的。 通常Task
涉及multithreading,不一定总是。
只记得TPL
知识你不能说你知道multithreading。 你需要覆盖很多概念。
- 线
- 同步原语
- 线程安全
- asynchronous编程
- 并行编程是TPL的一部分。
- 还有很多…
当然还有Task平行库。
注意:这不是完整的列表,这些只是从我的头顶。
要学习的资源:
- 埃里克的旧博客
- 埃里克的新博客
- Stephen toub的博客
对于单独的线程,我build议http://www.albahari.com/threading/
对于video教程,我build议Pluralsight 。 这是付出,但值得的成本。
最后但并非最不重要的:是的,当然是Stackoverflow 。
我的5分钱:你不必与Task.Run
或Task.Factory.StartNew
明确地进行线程Task.Factory.StartNew
,使你的TPL应用程序成为multithreading的。 考虑这个:
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();
doAsync
内部await
后的代码在不同的线程上同时执行。 以类似的方式,可以使用asynchronous套接字API, HttpClient
, Stream.ReadAsync
或使用线程池(包括IOCP池线程)的其他任何东西引入并发。
形象地说,每个.NET应用程序都是multithreading的,因为Framework广泛地使用了ThreadPool
。 即使一个简单的控制台应用程序也会为System.Diagnostics.Process.GetCurrentProcess().Threads.Count
显示多个线程。 你的面试官应该问你是否编写了并行 (或并行)代码。
- VSTS 2010 SGEN:错误:无法加载文件或程序集(从HRESULTexception:0x80131515)
- .Net Framework 4.0安装程序是否包含.Net Framework 3.5?
- 从string(reflection?)从C#dynamic对象获取属性值
- 为什么这个通用扩展方法不能编译?
- App.Config转换为Visual Studio 2010中不是Web项目的项目?
- Linq中的Row_number over(由xxx分区)?
- IIS 7.5上的ASP.NET MVC
- log4net不起作用
- 在.NET 4.0项目中引用.NET 2.0混合模式程序集时需要什么“附加configuration”?