【问题标题】:Why is my multi-threading slower than my single threading?为什么我的多线程比单线程慢?
【发布时间】:2015-07-09 17:23:51
【问题描述】:

我知道有几个人问过类似的问题,但我找不到任何可以让我理解为什么它变慢的答案。

所以,为了自己理解 Visual Studio 2013 中的线程对象,我制作了一个小控制台程序。我的 CPU 是 Intel Core i7,它可以使用多线程。

我的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {

        static TimeSpan MTTime;
        static TimeSpan STTime;

        static void Main(string[] args)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();


            Console.WriteLine(Environment.NewLine + "---------------Multi Process-------------" + Environment.NewLine);

            Thread th1 = new Thread(new ParameterizedThreadStart(Process));
            Thread th2 = new Thread(new ParameterizedThreadStart(Process));
            Thread th3 = new Thread(new ParameterizedThreadStart(Process));
            Thread th4 = new Thread(new ParameterizedThreadStart(Process));

            th1.Start("A");
            th2.Start("B");
            th3.Start("C");
            th4.Start("D");

            th1.Join();
            th2.Join();
            th3.Join();
            th4.Join();

            stopwatch.Stop();
            MTTime = stopwatch.Elapsed ;

            Console.WriteLine(Environment.NewLine + "---------------Single Process-------------" + Environment.NewLine);


            stopwatch.Reset();
            stopwatch.Start();

            Process("A");
            Process("B");
            Process("C");
            Process("D");

            stopwatch.Stop();
            STTime = stopwatch.Elapsed;

            Console.Write(Environment.NewLine + Environment.NewLine + "Multi  : "+ MTTime + Environment.NewLine + "Single : " + STTime);


            Console.ReadKey();
        }

        static void Process(object procName)
        {
            for (int i = 0; i < 100; i++)
            {
                Console.Write(procName);
            }
        }
    }
}

结果图片:

我们可以清楚地看到,多踩的过程是完全随机的,单踩只是一个接一个地压,但我认为这对速度没有影响。

起初,我认为我的线程只是比运行程序所需的进程大,但在更改为更大的进程后,单步执行仍然是最快的。那么,我是否错过了多线程中的一个概念?还是比较慢是正常的?

【问题讨论】:

  • 写入控制台是一个糟糕的测试。尝试在 Process 循环中计算几千个加密哈希值(最后一次写入控制台),您会看到加速。
  • 只是为了补充以下真正好的答案:使用线程很少会成功 - 请宁愿使用来自 TPL 的更高级别的 API,例如 Tasks,或者更好的是 PLinq 和 co。 (或者如果您真的知道自己在做什么,请继续 - 但没有冒犯,但在这种情况下,您可能想先深入了解一下)
  • 运行 Visual Studio Profiler,您将看到应用程序在哪里无效。 (提示:Console

标签: c# multithreading


【解决方案1】:

请注意,Process 写入控制台(基本上什么也不做),输出到控制台(这里充当一种共享资源)很慢,需要与其他线程同步。

据我了解,您使用的并行化会产生巨大的开销,但不会加快速度,因为线程显然大部分时间都在等待其他进程完成对控制台的写入。

【讨论】:

  • 没错。致 OP:如果您想要对算法的单线程与多线程实现进行有效比较,您必须确保这些实现实际上可以完全独立地运行。特别是,它们不能使用任何共享资源,因为这会导致各个线程不得不相互等待。
【解决方案2】:

来自官方Console 文档

使用这些流的 I/O 操作是同步的,这意味着 多个线程可以读取或写入流。这 表示通常是异步的方法,例如 TextReader.ReadLineAsync,如果对象同步执行 代表一个控制台流

这意味着控制台类处理线程同步,因此如果线程 A 和线程 B 尝试写入控制台,控制台将处理它们,并且只有一个 for time 可以写入。其背后的处理逻辑才是耗时较长的原因


更新
我建议你看看Parallel.ForEach

【讨论】:

  • 好的!,如果我理解,它是由控制台处理线程的方式引起的?所以,我想如果我在做其他事情然后在控制台中写或读东西会起作用吗?正确的 ?让我试试这个,但我不知道什么可能是一个很好的测试,谢谢你让我提前! :D
  • @Kazimar 当您使用一些算法或类似的东西处理大量数据时,您应该使用多线程,我还建议您看看 Parallel.ForEach,我会更新我的回答。看看,如果你认为我的答案就是你要找的答案,就标记为已回答!
  • 另一种描述方式是,因为控制台写入需要线程安全,它是一个序列化资源,线程花费大量时间等待轮到它们。如果这些是磁盘写入,您将从周日开始以六种方式缓冲它们。
【解决方案3】:

还有一点需要考虑:线程创建和连接的开销很大。此外,您可能会通过使用线程池获得轻微的性能提升,请看这里:

https://msdn.microsoft.com/en-us/library/system.threading.threadpool.queueuserworkitem%28v=vs.110%29.aspx

【讨论】:

    【解决方案4】:

    好的!感谢 Assa 和 Codor,让我把注意力放在正确的地方!我最后做了一个小控制台程序,非常清晰地显示一切。最后,在使用繁重的处理东西时,多线程会快很多。只需阅读我的代码,您就会轻松理解。

    结果:

    代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Threading;
    using System.Diagnostics;
    
    namespace ConsoleApplication1
    {
        class Program
        {
            //Timer for speed guidance
            static Stopwatch stopwatch;
            //Data i use for generate time
            static List<int> timeData;
            static void Main(string[] args)
            {
                stopwatch = new Stopwatch();
                timeData = new List<int> { 1000, 800, 200, 700, 600, 300, 800, 100, 200, 300, 655, 856, 695, 425 };
    
                ////-------------------------- SINGLE THREAD ------------------------------/////
                Console.WriteLine("-------------------------------------------------");
                Console.WriteLine("             Single Threading Process            ");
                Console.WriteLine("-------------------------------------------------");
                Console.WriteLine("   Process Time        Thread ID                 ");
                Console.WriteLine("-------------------------------------------------");
                stopwatch.Reset();
                stopwatch.Start();
                //For each normal that use only 1 thread
                foreach(int i in timeData)
                {
                    Process(i);
                }
    
                stopwatch.Stop();
                //Total time that the program take for making the process happen
                Console.WriteLine("*Total : " + stopwatch.Elapsed );
    
                ////-------------------------- Mulit Multiple ------------------------------/////
    
                Console.WriteLine("-------------------------------------------------");
                Console.WriteLine("-------------------------------------------------");
                Console.WriteLine("             Multi Threading Process            ");
                Console.WriteLine("-------------------------------------------------");
                Console.WriteLine("   Process Time        Thread ID                 ");
                Console.WriteLine("-------------------------------------------------");
                stopwatch.Reset();
                stopwatch.Start();
                //for each thats use Multiple thread fr the process (can be made with parallel.invoke or Task Library or Thread Library)
                Parallel.ForEach(timeData, (i) => Process(i));
                //Total time that the program take for making the process happen
                Console.WriteLine("*Total : " + stopwatch.Elapsed);
                Console.WriteLine("-------------------------------------------------");
                Console.ReadKey();
            }
    
            // Methode for sumulating long processing
            static void Process( int time)
            {
                stopwatch.Reset();
                stopwatch.Start();
                //sleep time simulate the IO portion of the process
                Thread.Sleep(time);
                // The loop simulate de algoritme type of precessing
                for (int i = 0; i < time*1000000; i++){}
                stopwatch.Stop();
                Console.WriteLine( stopwatch.Elapsed + "         " + Thread.CurrentThread.ManagedThreadId.ToString());          
            }
    
    
        }
    }
    

    【讨论】:

    • 这是一个更好的多线程“演示”。但是请注意,您的所有线程都使用相同的秒表对象。如果您希望每个线程测量它自己的执行时间,您应该在 Process(int) 中声明一个新的 StopWatch 变量。
    • 您的秒表只显示上次开始和第一次停止之间的差异。这就是为什么你有很多相同的数字。使用线程,每个单独的进程都不会更快。它是多个过程的组合。
    • 嗯,我不明白你的意思是什么?你可以开发更多plz :) 你是说我的开始/秒表在我的循环中的错误位置吗?因为如果你看到这个组合,我的多踩仍然更快。或者你只是说一个关于踩踏的事实,我的编码很好?
    • 请不要发布澄清原始问题的答案;发布对原始问题的编辑。答案应该只是问题的答案
    • 在这种特殊情况下,请忽略 Eric 的建议。编辑您的问题以应用其他答案中的建议会使未来的访问者无法理解现有答案。但是,我不建议发布“我尝试了另一个答案并且它有效”形式的答案,除非现有答案未能完全回答您的问题(尽管在这种情况下通常评论就足够了)。结束您的 StackOverflow 体验的理想方式是接受最有帮助的答案(单击该答案旁边的复选框)。
    【解决方案5】:

    当且仅当处理器成为瓶颈时,应用程序将使用多个线程更快地工作。有时您的磁盘或 RAM 是瓶颈。大多数情况下,它是数据库或外部服务。在这些情况下,添加多个线程不太可能使代码运行得更快,相反,由于额外的同步,它通常会减慢执行速度。

    如果你想让你的代码运行得更快,你必须首先检查它运行缓慢的原因。 分析您的代码。找到瓶颈

    记住Premature optimization is the root of all evil

    在您的情况下,瓶颈 - 阻止所有操作的最常访问的资源 - 是控制台输出流。它只有一个实例,并且运行速度相对较慢。即便是单个核心也不会 100% 用于尽可能快地在其上打印。

    【讨论】:

    • 是的,我终于注意到了我所有的测试!如果我运行一些小进程的东西,多线程的东西实际上会更慢,只是因为它自己生成它来制作多线程对象和everyting。在大多数情况下它没用....除非你做的东西使用真的非常多线程......像网络等......或大算法的东西。
    • 嗯,这个问题的答案并不完全,但我正在寻找我的应用运行缓慢的原因,这让我思考,所以 +1。
    【解决方案6】:

    如果你删除下面的行,你的多线程会更快。

    Console.Write(procName);
    

    这是因为操作系统将控制台视为共享资源,一次只有一个线程可以使用共享资源。所以在这种情况下,多线程加速就像单线程一样,但是多线程会负担一些额外的线程管理没有性能提升的开销。

    【讨论】:

      猜你喜欢
      • 2014-07-21
      • 1970-01-01
      • 2019-02-20
      • 2012-09-05
      • 1970-01-01
      • 1970-01-01
      • 2021-03-27
      • 2017-12-26
      相关资源
      最近更新 更多