【问题标题】:Differences between multithreading and multitasking in C# [duplicate]C#中多线程和多任务处理之间的区别[重复]
【发布时间】:2012-10-16 20:01:35
【问题描述】:

可能重复:
What is the difference between task and thread?

我知道标题本身可能看起来是一个重复的问题,但我确实阅读了与该主题相关的所有先前帖子,但仍然不太了解程序行为。

我目前正在编写一个检查大约 1,000 个电子邮件帐户的小程序。毫无疑问,我觉得多线程或多任务是正确的方法,因为每个线程/任务的计算成本并不高,但每个线程的持续时间很大程度上依赖于网络 I/O。

我认为在这种情况下,将线程/任务的数量设置为远大于内核数量的数字也是合理的。 (i5-750 四个)。因此,我将线程或任务的数量设置为 100。

使用Tasks编写的代码sn-p:

        const int taskCount = 100;
        var tasks = new Task[taskCount];
        var loopVal = (int) Math.Ceiling(1.0*EmailAddress.Count/taskCount);

        for (int i = 0; i < taskCount; i++)
        {
            var objContainer = new AutoCheck(i*loopVal, i*loopVal + loopVal);
            tasks[i] = new Task(objContainer.CheckMail);
            tasks[i].Start();
        }
        Task.WaitAll(tasks);

同样的代码sn-p用Threads写的:

        const int threadCount = 100;
        var threads = new Thread[threadCount];
        var loopVal = (int)Math.Ceiling(1.0 * EmailAddress.Count / threadCount);

        for (int i = 0; i < threadCount; i++)
        {
            var objContainer = new AutoCheck(i * loopVal, i * loopVal + loopVal);
            threads[i] = new Thread(objContainer.CheckMail);
            threads[i].Start();
        }
        foreach (Thread t in threads)
            t.Join();
        runningTime.Stop();
        Console.WriteLine(runningTime.Elapsed);

那么这两者的本质区别是什么?

【问题讨论】:

  • 据我了解,TaskScheduler 会将需要的线程数超过硬件所能提供的线程数的任务暂停,直到有足够的线程数可用于该任务。如果我理解正确的话,这个想法是,如果您有大量线程同时启动,它们将争夺硬件资源。任务不会发生的战斗。
  • @Marc-AndréJutras 这只是使用默认任务调度程序启动的任务的行为,它只是在线程池上运行它们。您可以拥有从不创建线程的任务,或者从不在任何其他线程中执行任何代码。
  • 两个都写了,为什么不都测试一下?
  • @MartinJames 好吧,他们都会工作。如果您不知道要查找什么,可能很难真正说出它们的运行方式有何不同。

标签: c# multithreading task-parallel-library


【解决方案1】:

任务不一定对应线程。它们将由任务库以比您的线程代码更有效的方式安排到线程池线程。

创建线程相当昂贵。任务将排队并在可用时重用线程,因此当一个线程正在等待网络 IO 时,它实际上可以被重用以执行另一个任务。闲置的线程是浪费的资源。您只能(同时)执行与您的处理器核心数相对应的线程数,因此 100 个线程意味着所有核心上的上下文切换至少 25 次。

如果使用任务,只需将所有 1000 个电子邮件处理任务排队,而不是将它们分批处理并让它撕裂。任务库将处理运行它的线程数。

【讨论】:

  • 这都适用于默认的任务调度器。您还可以使用自定义任务调度程序,拥有从 TaskCompletionSource 对象生成的任务等。
  • “如果使用任务,只需将所有 1000 个电子邮件处理任务排队,而不是将它们批量处理并让它撕裂。任务库将处理运行它的线程数。”意味着我可以将值“taskCount”更改为 1000 并放手?
  • 是的,他在他的例子中使用了默认的任务调度程序:)
  • @derekhh 是的,您可以这样做,任务库会为您安排这 1000 个任务到合适数量的线程中。
【解决方案2】:

排序快速的答案是任务不等于线程。一个任务在任务调度器中排队,然后在一个线程上执行,但排队 100 个任务并不意味着你将有 100 个线程在运行。

通常任务将在线程池中的一个线程上运行,该线程池的大小是有限的。一旦所有这些线程都忙了,那么您的任务将不得不等待一个线程变得可用于执行您的任务。也可以使用适当的任务调度程序将它们排队到 UI 线程等事物上,在这种情况下,它们最终由 UI 线程的消息循环同步运行(这在您与 UI 交互时很有用)。

在您的任务示例中,实际上不需要限制启动的任务数量,因为这些任务已经排入队列并且必须等到有可用的线程来运行它们。这可能是最好的方法,因为您让系统根据系统可以处理的内容来确定最大线程数,而不是假设您有足够的 CPU/内存来处理它。

而您使用线程的示例特别意味着您肯定会增加您分配的线程数。

【讨论】:

  • "A task is queued up in a Task Scheduler and then executed on a Thread" 可能,也可能不是。它可能根本不会在另一个线程上执行。一项任务基本上只是在说,“有一些任务将在以后完成。我会告诉你什么时候完成,它的结果是什么(如果有的话)。
  • @Servy 我说的是“在线程上执行”而不是另一个线程。它总是在一个线程上执行,无论是 UI 线程、任务池线程还是其他线程。
  • 不。您假设任务是使用委托开始的,但情况并非总是如此。想象一下使用TaskCompletionSource 创建的任务。从技术上讲,它根本不需要执行任何操作。
  • @Servy 我认为您过于字面意思,代码必须在某个线程上执行。如果没有要执行的代码,那么我认为这是一个假设,任何地方都没有发生任何事情。我的回答是对运行任务和运行线程之间区别的一般概述,而不是解释 TPL 的每一个细微差别的深入教程。
  • 即使你没有解释每一个细微差别,你从根本上将任务解释为你执行的事情;它是run 的一些方法或委托。这并不总是正确的。它可能是真的,它是更常见的例子之一,但还有其他类型的任务,例如,想象Task.WhenAll返回的Task。该任务不会在任何地方运行。它的结果取决于在某处运行的 other 任务,但它没有要运行的代码。即使您没有解释那里差异的后果,您对任务的总结也是有缺陷的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-09
  • 1970-01-01
  • 2011-03-16
相关资源
最近更新 更多