std::映射多线程中奇怪的资源争用

std::map strange resource contention in multithreading

本文关键字:资源 争用 映射 多线程 std      更新时间:2023-10-16

我有一个std::map的奇怪行为(或者std::set,在这个场景中它们的行为似乎是一样的(。这可能是我对这应该如何工作有一个严重的误解我使用的是VS2010 SP1。

以这个函数为例:

extern time_t g_nElapsed;
UINT Thread(LPVOID _param)
{
    UINT nRuns = (UINT)_param;
    for(UINT i=0; i<nRuns; ++i)
    {
        time_t _1 = time(NULL);
        std::set<UINT> cRandomSet;
        cRandomSet.insert(1);
        cRandomSet.insert(2);
        cRandomSet.insert(3);
        cRandomSet.insert(4);
        g_nElapsed += (time(NULL) - _1);
    }

    return 0;
}

现在,如果我运行8个线程,每个线程有100000次迭代,g_nElpsed大约需要40秒。如果我用800000次迭代运行1个线程,g_nElpsed大约是5秒。我的印象是,对于任何合理数量的线程,g_nElpsed都应该大致相同。可以这么说。。。即使工作保持不变,处理器的使用量也会随着线程数量的增加而增加。然而,似乎与集合的某种资源争用会导致运行时增加。但为什么呢?这是线程本地。。。

我确信这是一个简单的误解和简单的解决方案,但我不太确定这里的问题是什么

以下代码没有表现出这种行为:

extern time_t g_nElapsed;
UINT Thread(LPVOID _param)
{
    UINT nRuns = (UINT)_param;
    for(UINT i=0; i<nRuns; ++i)
    {
        time_t _1 = time(NULL);
        UINT n[4];
    n[0] = 1;
        n[1] = 1;
        n[2] = 1;
        n[3] = 1;
        g_nElapsed += (time(NULL) - _1);
    }

    return 0;
}

您正在创建和销毁许多容器,每个容器都使用operator new来分配内存。在许多系统上,这需要同步来管理像您这样典型的小分配中分配的可用内存。因此,您可能会在那里引发相当多的线程间争用。

您可以尝试不同的分配器,例如tcmalloc(http://goog-perftools.sourceforge.net/doc/tcmalloc.html)。它是专门为处理这个问题而设计的。

另一种方法是使用对象池或其他分配策略来避免完全使用标准分配机制。这将需要一些代码更改,而使用tcmalloc则不需要。