C# 分配内存时的线程争用
在C#中,我运行了一个创建许多小对象的玩具代码(我知道理想情况下应该避免使用这些对象——我只想研究这个问题)。对于创建的对象总数相同,每个处理器一个线程的运行速度比一个线程快(Parallel.For) 原子操作包括创建一个包含20k个小对象的列表(实际上是一个数组)(为简单起见,此处为long[4]):C# 分配内存时的线程争用,c#,multithreading,memory-management,C#,Multithreading,Memory Management,在C#中,我运行了一个创建许多小对象的玩具代码(我知道理想情况下应该避免使用这些对象——我只想研究这个问题)。对于创建的对象总数相同,每个处理器一个线程的运行速度比一个线程快(Parallel.For) 原子操作包括创建一个包含20k个小对象的列表(实际上是一个数组)(为简单起见,此处为long[4]): private static void CreateList() { 长[][]列表=新长[20000][]; 对于(变量i=0;i
private static void CreateList()
{
长[][]列表=新长[20000][];
对于(变量i=0;i<20000;i++)
列表[i]=新长[4];
}
如果我在一个线程中创建1000个列表,它将在1.5秒内运行。如果我用几个线程创建1000个列表(每个线程负责1000个列表中的一个子集),它将在2秒内运行
在以下情况下,行为基本相同:
- 使用经典的小对象代替长对象[4]
- 使用实数列表而不是数组
- 使用不同数量的对象
public static void Main()
{
Benchmark(1000, CreateList);
}
private static void Benchmark(int repeat, Action action)
{
Console.WriteLine("Single thread");
Benchmark(delegate ()
{
for (int i = 0; i < repeat; i++)
action();
});
Console.WriteLine("Multi thread");
Benchmark(delegate ()
{
Parallel.For(0, repeat, i => action());
});
}
private static void Benchmark(Action action)
{
for (int i = 0; i < 10; i++)
{
Stopwatch sw = new Stopwatch();
sw.Start();
action();
sw.Stop();
Console.WriteLine("Time : " + sw.Elapsed.TotalSeconds);
}
}
publicstaticvoidmain()
{
基准(1000,CreateList);
}
私有静态void基准(int repeat,Action-Action)
{
控制台写入线(“单线程”);
基准(委托人()
{
for(int i=0;iaction());
});
}
私有静态无效基准(操作)
{
对于(int i=0;i<10;i++)
{
秒表sw=新秒表();
sw.Start();
动作();
sw.Stop();
Console.WriteLine(“时间:+sw.passed.TotalSeconds”);
}
}
尽管内存管理器使用某种信号量是正常的,但具有许多内存分配的多线程应用程序在默认的C#垃圾收集器中工作得非常糟糕。有了适当的垃圾收集器,情况会好得多
你应该:
- 启用服务器GC
- (可能)禁用并发GC
<runtime>
<gcServer enabled="true"/>
<gcConcurrent enabled="false" />
</runtime>
您可以在中阅读有关服务器和工作站GC的详细信息。当然,内存管理本质上是单线程的。分配和收集。否则,在尝试分配相同的内存时,您将陷入竞争状态!除了上面所说的,不确定这是否也是您要问的另一件事,仅仅因为您生成了多个线程,并不一定意味着它会运行得更快。创建线程本身就有一个开销,因此对于非常短的任务,并行运行并不总是导致较小的执行时间。我也同意npinti所说的。我喜欢说“多线程必须仔细挑选它的问题”。否则,您将得到比单个线程更复杂(容易出错)、使用更多内存和更慢的代码。平行减速是一件事()。一点也不能用多线程处理的问题。只要尝试多线程处理斐波那契序列,使每个数字都由一个单独的线程创建即可。@npitni;线程不会在此处运行短任务。没有开销。用数学代替内存分配,你会看到的。@BenoitSanchez:如果我用CPU绑定的操作代替内存绑定的操作,它甚至不再是类似的代码了。
<runtime>
<gcServer enabled="true"/>
<gcConcurrent enabled="false" />
</runtime>