我在书中读到线程创建是昂贵的(不像过程创建那么昂贵,但它是),我们应该避免它。我编写测试代码,我很惊讶线程创建的速度有多快。
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.
我的代码是错误的还是线程创建如此之快以及书籍中的建议是错误的? (我看到每个线程只有一些额外的内存消耗但没有创建时间。)
线程创建非常慢。考虑这段代码,这些代码与内联服务的速度形成鲜明对比,并使用多个线程进行操作:
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(无需调试即可启动)。