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

C# 使用任务(TPL)库是否使应用程序成为多线程的?,c#,multithreading,.net-4.0,task-parallel-library,multitasking,C#,Multithreading,.net 4.0,Task Parallel Library,Multitasking,最近在接受采访时,我得到了这个问题 问:您编写过多线程应用程序吗 A:是的 问:想解释一下吗 答:我使用任务(任务并行库)来执行一些任务,比如在加载UI时等待来自internet的一些信息。这提高了我的应用程序的可用性 问:但是,仅仅您使用了TPL就意味着您已经编写了一个多线程应用程序 我:(不知道该说什么) 那么,什么是多线程应用程序呢?它与使用任务不同吗?A任务是对未来工作的承诺。使用它时,您可以将它用于基于I/O的工作,而不需要使用多个线程来执行代码。一个很好的例子是将C#5的async/

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

问:您编写过多线程应用程序吗

A:是的

问:想解释一下吗

答:我使用
任务
(任务并行库)来执行一些任务,比如
在加载UI时等待来自internet的一些信息。这提高了我的应用程序的可用性

问:但是,仅仅您使用了
TPL
就意味着您已经编写了一个
多线程
应用程序

我:(不知道该说什么)


那么,什么是多线程应用程序呢?它与使用
任务
不同吗?

A
任务
是对未来工作的承诺。使用它时,您可以将它用于基于
I/O的
工作,而不需要使用多个线程来执行代码。一个很好的例子是将C#5的
async/await
特性与
HttpClient
一起使用,它可以进行基于网络的I/O工作

但是,您可以利用
TPL
来执行多线程工作。例如,当使用
Task.Run
Task.Factory.Startnew
启动新任务时,后台工作会在
ThreadPool
上排队等待您,而
TPL
会为我们抽象出线程池,允许您使用多个线程

使用多线程的一个常见场景是当您有CPU限制的工作可以同时(并行)完成时。使用多线程应用程序需要承担很大的责任

所以我们看到,使用
TPL
并不一定意味着使用多线程,但您肯定可以利用它来执行多线程

问:但是,你使用TPL就意味着你写了一个 多线程应用程序

聪明的问题,任务!=使用多线程,您可以创建一个
任务
,该任务本身可以在单个线程(可能仅是UI线程)中执行

任务
只是对可能在将来完成的操作的抽象。这并不意味着代码是多线程的。通常,
任务
涉及多线程,但不一定总是这样

请记住,只有掌握了
TPL
的知识,您才能说您了解多线程。您需要涵盖许多概念

  • 线
  • 同步原语
  • 线程安全
  • 异步编程
  • 并行编程是第三方物流的一部分
  • 还有更多
当然还有任务并行库

注意:这不是完整的列表,这些只是我的想法


学习资源:

我建议单就线程而言

对于视频教程,我建议。它是有报酬的,但值得付出代价

最后但并非最不重要的一点:是的,当然是这样。

任务可以用来表示在多个线程上进行的操作,但它们不必这样做。可以编写只在单个线程中执行的复杂TPL应用程序。例如,当您有一个任务表示对某些数据的网络请求时,该任务不会创建额外的线程来实现该目标。这样的程序(希望)是异步的,但不一定是多线程的

并行性是在同一时间做不止一件事。这可能是多线程的结果,也可能不是

我们来做个类比


以下是鲍勃如何烹调晚餐:

  • 他把一壶水倒满,然后煮开
  • 然后他把意大利面放入水中
  • 面食吃完后,他把它吸干
  • 他准备酱汁的配料
  • 他把酱汁的所有原料都放在平底锅里
  • 他煮酱汁
  • 他把酱汁放在意大利面上
  • 他吃晚饭
  • Bob做饭时完全同步,没有多线程、异步或并行


    以下是简做晚餐的方式:

  • 她把一壶水倒满,开始煮
  • 她准备酱汁的配料
  • 她把意大利面放在沸水里
  • 她把配料放在平底锅里
  • 她喝干了面食
  • 她把酱汁放在面食上
  • 她吃晚饭
  • Jane利用异步烹饪(没有任何多线程)在烹饪晚餐时实现并行性


    下面是Servy如何烹饪晚餐:

  • 他告诉鲍勃烧一壶水,准备好后放入意大利面,然后端上意大利面
  • 他告诉简准备酱汁的原料,烹饪,然后在面食上食用
  • 他等鲍勃和简说完
  • 他吃晚饭
  • Servy利用了多个线程(工作线程),每个线程(工作线程)分别同步地完成各自的工作,但彼此异步地工作以实现并行性

    当然,如果我们考虑,例如,我们的炉子有两个燃烧器,或者只有一个,这就更有趣了。如果我们的炉子有两个燃烧器,那么我们的两条线,鲍勃和简,都能在不妨碍彼此的情况下完成工作。他们可能会撞上一些肩膀,或者时不时地试图从同一个柜子里抓东西,所以他们会放慢速度,但不会太慢。但是,如果他们每个人都需要共用一个炉灶,那么无论其他人何时在工作,他们实际上都无法完成很多工作。在这种情况下,这项工作实际上不会比让一个人完全同步做饭更快,就像鲍勃独自做饭时那样。在本例中,我们使用多个线程进行烹饪,但我们的烹饪不是并行的。并非所有多线程工作实际上都是并行工作。这就是在一台机器上运行多个线程时发生的情况
    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);
        }
    }
    
    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);
        }
    }
    
    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();