多线程比单线程慢

Kai*_*ann 9 c# performance multithreading

我有以下代码(控制台应用程序的'Program.cs'的完整内容).'countUp'直到'countUp4'的单线程执行需要13秒,多线程执行21秒.

我有一个Intel Core i5-2400 @ 3.10 GHz,8 GB Ram,Windows 7 64 Bit.那么为什么mutli线程执行比单线程执行慢?

多线程对于不阻塞简单c#应用程序的主程序有用吗?多线程什么时候给我一个执行速度的优势?

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

namespace ConsoleApplication1
{
    class Program
    {
        static int counter = 0;
        static int counter2 = 0;
        static int counter3 = 0;
        static int counter4 = 0;

        static void Main(string[] args)
        {
            Console.WriteLine("Without multithreading:");
            Console.WriteLine("Start:" + DateTime.Now.ToString());

            countUp();
            countUp2();
            countUp3();
            countUp4();

            Console.WriteLine("");
            Console.WriteLine("With multithreading:");
            Console.WriteLine("Start:" + DateTime.Now.ToString());

            Thread thread1 = new Thread(new ThreadStart(countUp));
            thread1.Start();
            Thread thread2 = new Thread(new ThreadStart(countUp2));
            thread2.Start();
            Thread thread3 = new Thread(new ThreadStart(countUp3));
            thread3.Start();
            Thread thread4 = new Thread(new ThreadStart(countUp4));
            thread4.Start();

            Console.Read();
        }

        static void countUp()
        {
            for (double i = 0; i < 1000000000; i++)
            {
                counter++;
            }

            Console.WriteLine(counter.ToString());
            Console.WriteLine(DateTime.Now.ToString());
        }

        static void countUp2()
        {
            for (double i = 0; i < 1000000000; i++)
            {
                counter2++;
            }

            Console.WriteLine(counter2.ToString());
            Console.WriteLine(DateTime.Now.ToString());
        }

        static void countUp3()
        {
            for (double i = 0; i < 1000000000; i++)
            {
                counter3++;
            }

            Console.WriteLine(counter3.ToString());
            Console.WriteLine(DateTime.Now.ToString());
        }

        static void countUp4()
        {
            for (double i = 0; i < 1000000000; i++)
            {
                counter4++;
            }

            Console.WriteLine(counter4.ToString());
            Console.WriteLine(DateTime.Now.ToString());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

And*_*tan 20

这是一个你可能看不到的原因:假共享,因为这4个整数都在内存中并排存在.

更新 - 前几年的MSDN mags现在仅作为.chm文件提供- 所以你必须从这里获取MSDN Mag'2008年10月版,并且在下载之后,你必须记住右击并"解锁"文件打开它之前,Windows资源管理器中的文件属性对话框(其他操作系统可用!).您正在寻找由Stephen Toub,Igor Ostrovsky和Huseyin Yildiz撰写的名为".Net Matters"的专栏

这篇文章(全部阅读 - 很精彩)展示了内存中并排的值如何在更新时最终导致阻塞,因为它们都位于同一个缓存行中.这是非常低级别的阻止,您无法从.Net代码禁用.但是,您可以强制将数据间隔得更远,以便保证或至少增加每个值将位于不同缓存行上的可能性.

这篇文章使用数组 - 但它可能会影响你.

要跟进下面的建议,您可以通过稍微更改代码来证明/反驳这一点:

class Program 
{ 
    class CounterHolder {
       private int[] fakeInts = new int[1024];
       public int Value = 0;
    }
    static CounterHolder counter1 = new CounterHolder(); 
    static CounterHolder counter2 = new CounterHolder(); 
    static CounterHolder counter3 = new CounterHolder(); 
    static CounterHolder counter4 = new CounterHolder(); 
Run Code Online (Sandbox Code Playgroud)

然后修改您的线程函数以操纵Value每个计数器持有者的公共字段.

我让那些阵列真的比它们需要的要大得多,希望它能证明它更好:)

  • 应该很容易证明,@ KaiHartmann,你为什么不把每个int增加到每个方法的局部增量,看看它是否有所作为. (2认同)

Moo*_*ght 5

Andreas Zaltan就是答案.拿代码

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

namespace ConsoleApplication1
{
    class Program
    {
        //static int counter = 0;
        //static int counter2 = 0;
        //static int counter3 = 0;
        //static int counter4 = 0;

        class CounterHolder
        {
            private int[] fakeInts = new int[1024];
            public int Value = 0;
        }
        static CounterHolder counter1 = new CounterHolder();
        static CounterHolder counter2 = new CounterHolder();
        static CounterHolder counter3 = new CounterHolder();
        static CounterHolder counter4 = new CounterHolder(); 

        static void Main(string[] args)
        {
            Console.WriteLine("Without multithreading:");
            Console.WriteLine("Start: " + DateTime.Now.ToString());

            Stopwatch sw = new Stopwatch();
            sw.Start();

            countUp();
            countUp2();
            countUp3();
            countUp4();

            sw.Stop();
            Console.WriteLine("Time taken = " + sw.Elapsed.ToString());

            Console.WriteLine("\nWith multithreading:");
            Console.WriteLine("Start: " + DateTime.Now.ToString());
            sw.Reset();
            sw.Start();

            Task task1 = Task.Factory.StartNew(() => countUp());
            Task task2 = Task.Factory.StartNew(() => countUp2());
            Task task3 = Task.Factory.StartNew(() => countUp3());
            Task task4 = Task.Factory.StartNew(() => countUp4());
            var continuation = Task.Factory.ContinueWhenAll(new[] { task1, task2, task3, task4 }, tasks =>
            {
                Console.WriteLine("Total Time taken = " + sw.Elapsed.ToString());
            });
            Console.Read();
        }

        static void countUp()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (double i = 0; i < 1000000000; i++)
                counter1.Value++;
            sw.Stop();
            Console.WriteLine("Task countup took: " + sw.Elapsed.ToString());
        }

        static void countUp2()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (double i = 0; i < 1000000000; i++)
                counter2.Value++;
            sw.Stop();
            Console.WriteLine("Task countUP2 took: " + sw.Elapsed.ToString());
        }

        static void countUp3()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (double i = 0; i < 1000000000; i++)
                counter3.Value++;
            sw.Stop();
            Console.WriteLine("Task countUP2 took: " + sw.Elapsed.ToString());
        }

        static void countUp4()
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (double i = 0; i < 1000000000; i++)
                counter4.Value++;
            sw.Stop();
            Console.WriteLine("Task countUP2 took: " + sw.Elapsed.ToString());
        }
    } 
}
Run Code Online (Sandbox Code Playgroud)

使用整数运行它,你得到的多线程版本运行速度稍慢.

Serial: 13.88s
Multi-threaded: 14.01
Run Code Online (Sandbox Code Playgroud)

使用上面的建议运行它,您将获得以下内容

在此输入图像描述

为了清楚起见,我发布了这个...

  • 很高兴看到结果:) (3认同)