10

I have the following Code (complete content of 'Program.cs' of console application). The single threaded execution of 'countUp' till 'countUp4' takes 13 sec., the multi threaded execution 21 sec..

I have a Intel Core i5-2400 @ 3.10 GHz, 8 GB Ram, Windows 7 64 Bit. So why is the multi threaded execution slower than the single threaded one?

Is multithreading just useful for not blocking the main routine of simple c# applications? When does multithreading give me an advantage in execution speed?

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());
        }
    }
}
4

6 回答 6

21

这是您可能看不到的原因:错误共享,因为这 4 个整数在内存中都并排放置。

更新 - 前几年的 MSDN 杂志现在仅作为.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(); 

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

我已经使这些数组比他们需要的要大得多,希望它能证明它更好:)

于 2012-09-12T14:37:48.497 回答
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());
        }
    } 
}

用整数运行它,你会得到多线程版本的运行速度稍微慢一些。

Serial: 13.88s
Multi-threaded: 14.01

使用上面的建议运行它,您会得到以下信息

在此处输入图像描述

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

于 2012-09-12T15:05:59.483 回答
1

我用秒表重写了你的代码。在我的计算机上,多线程比单线程更快(以下时间)。

此外,您需要在线程上调用Join方法以确保它们在退出程序之前完成。

没有多线程时经过的时间:: 00:00:21.6897179

多线程经过的时间:: 00:00:14.7893703

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

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)
        {
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

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

            stopwatch.Stop();
            Console.WriteLine("Time elapsed without multithreading:: {0}",
        stopwatch.Elapsed);

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

            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();

            thread1.Join();
            thread2.Join();
            thread3.Join();
            thread4.Join();

            stopwatch.Stop();
            Console.WriteLine("Time elapsed with multithreading:: {0}",
        stopwatch.Elapsed);

            Console.Read();
        }

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

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

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

        static void countUp4()
        {
            for (double i = 0; i < 1000000000; i++)
            {
                counter4++;
            }
        }
    }
}
于 2012-09-12T14:45:55.327 回答
0

首先,使用 System.Runtime.Diagnostic 命名空间中的 StopWatch 类而不是 DateTime 进行测量。

其次,您在同步执行后不清除“计数器”。

您应该对所有线程使用并行化,比它更快!初始化新线程的成本很高。顺便说一句,您可以使用 ThreadPool。

于 2012-09-12T14:35:48.980 回答
0

正如 Joeb454 所说,在这种情况下你必须寻找并行处理。您的多线程只会减慢执行速度,因为创建新线程需要“很长时间”。

于 2012-09-12T14:40:02.477 回答
0

我不是多线程方面的专家,但我认为您在那里所做的基本上只是将工作从 UI 线程中移开。

如果您有一些长时间运行或密集的工作要做,这绝不是一件坏事,因为它允许您为最终用户保留响应式 UI。为了更快地运行这样的事情,如果我的记忆正确地为我服务,您将需要研究并行处理。

于 2012-09-12T14:32:07.347 回答