C# 分配内存时的线程争用

C# 分配内存时的线程争用,c#,multithreading,memory-management,C#,Multithreading,Memory Management,在C#中,我运行了一个创建许多小对象的玩具代码(我知道理想情况下应该避免使用这些对象——我只想研究这个问题)。对于创建的对象总数相同,每个处理器一个线程的运行速度比一个线程快(Parallel.For) 原子操作包括创建一个包含20k个小对象的列表(实际上是一个数组)(为简单起见,此处为long[4]): private static void CreateList() { 长[][]列表=新长[20000][]; 对于(变量i=0;i

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

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

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
由于内存分配部分独立,服务器GC将允许线程之间更好的并行化。在这种情况下,多核机器的性能可能会发生根本性的变化

简而言之,将以下内容添加到项目的配置文件中:

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


您可以在中阅读有关服务器和工作站GC的详细信息。

当然,内存管理本质上是单线程的。分配和收集。否则,在尝试分配相同的内存时,您将陷入竞争状态!除了上面所说的,不确定这是否也是您要问的另一件事,仅仅因为您生成了多个线程,并不一定意味着它会运行得更快。创建线程本身就有一个开销,因此对于非常短的任务,并行运行并不总是导致较小的执行时间。我也同意npinti所说的。我喜欢说“多线程必须仔细挑选它的问题”。否则,您将得到比单个线程更复杂(容易出错)、使用更多内存和更慢的代码。平行减速是一件事()。一点也不能用多线程处理的问题。只要尝试多线程处理斐波那契序列,使每个数字都由一个单独的线程创建即可。@npitni;线程不会在此处运行短任务。没有开销。用数学代替内存分配,你会看到的。@BenoitSanchez:如果我用CPU绑定的操作代替内存绑定的操作,它甚至不再是类似的代码了。
  <runtime>
    <gcServer enabled="true"/>
    <gcConcurrent enabled="false" />
  </runtime>