首页 > 解决方案 > 分配内存时的线程争用

问题描述

在 C# 中,我运行了一个创建许多小对象的玩具代码(我知道理想情况下应该避免这种情况——我只是想研究这个问题)。对于相同数量的创建对象,一个线程比每个处理器一个线程运行得更快 (Parallel.For)。

原子操作包括创建一个包含 20k 小对象的列表(实际上是一个数组)(为简单起见,此处为 long[4]):

private static void CreateList()
{
    long[][] list = new long[20000][];
    for (var i = 0; i < 20000; i++)
        list[i] = new long[4];
}

如果我在单个线程中创建 1000 个列表,它会在 1.5 秒内运行。如果我用多个线程创建 1000 个列表(每个线程负责 1000 个列表的一个子集),它会在 2 秒内运行。

在以下情况下,行为基本相同:

你能解释一下为什么吗?内存管理器中是否有“锁”。它与垃圾收集有关吗?

代码详情:

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

标签: c#multithreadingmemory-management

解决方案


尽管内存管理器使用某种信号量是正常的,但具有许多内存分配的多线程应用程序在默认的 C# 垃圾收集器中工作得非常糟糕。使用适当的垃圾收集器,事情会好得多。

你应该:

  • 启用服务器 GC
  • (可能)禁用并发 GC

服务器 GC 将允许线程之间更好程度的并行化,因为内存分配是部分独立的。在这种情况下,多核机器的性能会发生根本性的变化。

简而言之,将其添加到项目的配置文件中:

  <runtime>
    <gcServer enabled="true"/>
    <gcConcurrent enabled="false" />
  </runtime>

您可以在垃圾收集基础中阅读有关服务器和工作站 GC 的详细信息。


推荐阅读