我在书中读到线程创建很昂贵(不像创建流程那么昂贵,但它确实如此)我们应该避免它。我编写了测试代码,我对创建线程的速度感到震惊。
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace ConsoleApplication1
{
class Program
{
static int testVal = 0;
static void Main(string[] args)
{
const int ThreadsCount = 10000;
var watch = Stopwatch.StartNew();
for (int i = 0; i < ThreadsCount; i++)
{
var myThread = new Thread(MainVoid);
myThread.Start();
}
watch.Stop();
Console.WriteLine("Test value ={0}", testVal);
Console.WriteLine("Ended in {0} miliseconds", watch.ElapsedMilliseconds);
Console.WriteLine("{0} miliseconds per thread ", (double)watch.ElapsedMilliseconds / ThreadsCount);
}
static void MainVoid()
{
Interlocked.Increment(ref testVal);
}
}
}
输出:
Test value =10000
Ended in 702 miliseconds
0,0702 miliseconds per thread.
我的代码是错误的还是线程创建如此之快以及书籍中的建议是错误的? (我看到每个线程只有一些额外的内存消耗但没有创建时间。)
答案 0 :(得分:2)
创建线程非常慢。考虑这段代码,这些代码与内联服务的速度形成对比,并且可以使用多个线程进行操作:
private void DoStuff()
{
const int ThreadsCount = 10000;
var sw = Stopwatch.StartNew();
int testVal = 0;
for (int i = 0; i < ThreadsCount; ++i)
{
Interlocked.Increment(ref testVal);
}
sw.Stop();
Console.WriteLine(sw.ElapsedTicks);
sw = Stopwatch.StartNew();
testVal = 0;
for (int i = 0; i < ThreadsCount; ++i)
{
var myThread = new Thread(() =>
{
Interlocked.Increment(ref testVal);
});
myThread.Start();
}
sw.Stop();
Console.WriteLine(sw.ElapsedTicks);
}
在我的系统上,内联执行需要200个刻度。有了线程,它几乎有200万个滴答。因此,使用线程需要大约10,000倍的时间。我在这里使用ElapsedTicks
而不是ElapsedMilliseconds
,因为ElapsedMilliseconds
内联代码的输出为0.线程版本大约需要700毫秒。上下文切换很昂贵。
此外,您的测试存在根本缺陷,因为您在获取结果之前没有明确等待所有线程完成。在最后一个线程完成递增之前,你很可能输出testVal
的值。
顺便说一下,当你没有安装调试器时,你应该确保在发布模式下运行它。在Visual Studio中,使用Ctrl + F5(无需调试即可启动)。