我有以下代码(控制台应用程序的'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());
}
}
}
答案 0 :(得分:20)
以下是您可能看不到的原因:false sharing因为这4个整数并排在内存中。
更新 - 前几年的MSDN mags现在仅作为.chm
文件提供 - 因此您必须抓住'October 2008' edition of the MSDN Mag from here,并且在下载后,您必须记住右键单击并且'在打开之前,从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
。
我让那些阵列真的比它们需要的大得多,希望它能证明它更好:)
答案 1 :(得分: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());
}
}
}
使用intergers运行它,你得到的多线程版本运行速度稍慢。
Serial: 13.88s
Multi-threaded: 14.01
使用上面的建议运行它,您将获得以下内容
为了清楚起见,我发布了这个...
答案 2 :(得分:1)
我用StopWatch重写了你的代码。多线程比我的计算机上的单线程更快(次数以下)。
此外,您需要在线程上调用 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++;
}
}
}
}
答案 3 :(得分:0)
我不是多线程专家,但我认为你在那里所做的只是将工作从UI线程中移开。
如果您有一些长期或密集的工作要做,这绝不是一件坏事,因为它允许您为最终用户保留响应式UI。为了更快地运行这样的事情,如果我的记忆能够正确地为我服务,你将需要研究并行处理。
答案 4 :(得分:0)
首先,使用System.Runtime.Diagnostic命名空间中的StopWatch类进行测量,而不是使用DateTime。
其次,在同步执行后,您不会清除“计数器”。
你应该对所有线程使用并行化,而不是更快!初始化新线程的成本很高。顺便说一下,你可以使用ThreadPool。
答案 5 :(得分:0)
正如Joeb454所说,在这种情况下你必须寻找并行处理。 您的多线程只会减慢执行速度,因为创建一个新线程会花费很长时间。