最近接受采访时,我得到了这个问题。
问:你有没有编写多线程应用程序?答:是的
问:关心更多解释?
答:我使用Tasks
(任务并行库)来执行waiting for some info from internet while loading UI
之类的任务。这提高了我的应用程序可用性。
问:但是,您使用TPL
表示您已经编写了multithreaded
个应用程序?
我:(不知道该说些什么1)
那么,什么是多线程应用程序呢?它与使用Tasks
不同吗?
答案 0 :(得分:98)
任务可以用于表示在多个线程上发生的操作,但它们没有。一个可以编写只能在单个线程中执行的复杂TPL应用程序。例如,当您有一项任务表示某些数据的网络请求时,该任务不将创建其他线程来实现该目标。这样的程序(希望)是异步的,但不一定是mutlithreaded。
并行性同时做了不止一件事。这可能是也可能不是多线程的结果。
我们在这里进行类比。
这是鲍勃如何做晚餐的方式:
以下是Jane如何做饭:
Jane利用异步烹饪(没有任何多线程)来烹饪晚餐时实现并行性。
以下是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
来创建表示在另一个线程中工作的任务。他启动了两个不同的工人,让他们各自同时做一些工作,然后等待两个工人完成。
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);
}
}
答案 1 :(得分:4)
Task
是对未来工作的承诺。使用它时,您可以将其用于I/O based
工作,不要求您使用多个线程执行代码。一个很好的例子是使用C {5的async/await
和HttpClient
的功能来完成基于网络的I / O工作。
但是,您可以利用TPL
执行多线程工作。例如,当使用Task.Run
或Task.Factory.Startnew
开始新任务时,幕后工作会在ThreadPool
上为您排队,TPL
为我们摘录,允许你使用多个线程。
使用多个线程的常见情况是,当您具有可以同时(并行)完成的CPU绑定工作时。使用多线程应用程序需要承担很大的责任。
所以我们看到使用TPL
并不意味着使用多个线程,但你绝对可以利用它来进行多线程。
答案 2 :(得分:3)
问:但是,只是你使用过TPL意味着你已经写过了 多线程应用程序?
智能问题,任务!=多线程,使用TaskCompletionSource您可以创建一个Task
,它可以在单个线程中执行(可能只是UI线程)本身。
Task
只是对未来可能完成的操作的抽象。这并不意味着代码是多线程的。通常Task
涉及多线程,不一定总是如此。
请记住只有TPL
的知识,你不能说你知道多线程。您需要涵盖许多概念。
当然还有Task并行库。
注意:这不是完整列表,这些只是我的头脑。
<小时/> 学习资源:
仅针对线程,我建议http://www.albahari.com/threading/
对于视频教程,我建议Pluralsight。它是有偿但值得的。
最后但并非最不重要:当然是,Stackoverflow。
答案 3 :(得分:1)
我的5美分:您不必明确地与Task.Run
或Task.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();
await
内的doAsync
之后的代码在不同的线程上同时执行。以类似的方式,可以使用异步套接字API,HttpClient
,Stream.ReadAsync
或使用线程池的任何其他内容(包括IOCP池线程)引入并发性。
形象地说,每个.NET应用程序都已经是多线程的,因为Framework广泛使用ThreadPool
。即使是简单的控制台应用程序也会为System.Diagnostics.Process.GetCurrentProcess().Threads.Count
显示多个线程。您的面试官应该问您是否已编写并发(或并行)代码。