2013-02-26 34 views
0

我有一个奇怪的行为与std :: map(或std :: set,他们似乎在这种情况下行为相同)。 这可能是我对这应该如何工作的严重误解。 我正在使用VS2010 SP1。std :: map多线程中奇怪的资源争用

就拿这个功能:

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_nElapsed将大致40秒。 如果我用800,000次迭代运行1个线程,g_nElapsed约为5秒。 我的印象是g_nElapsed对于任何合理数量的线程应该大致相同。 可以这么说......即使工作保持不变,处理器使用率也会随着线程数量的增加而增加。 但是,似乎与该集合的某种资源争用导致运行时增加。 但是为什么?这是线程本地...

我敢肯定这是一个简单的误解和一个简单的修复,但我不太确定问题在这里。

下面的代码不会出现此行为:

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; 
} 
+1

它的分配器锁定在创建/销毁对象'cRandomSet'上。另外,每个'cRandomSet.insert'都可以调用** new **,所以它的锁也是。 – PSIAlt 2013-02-26 12:49:45

+0

内存分配的(不那么)美丽的一面...... – 2013-02-26 12:58:46

回答

3

你正在创建和销毁许多容器,每一个使用operator new分配内存。在许多系统上,这需要同步来管理通常的小型分配(如您的)所分配的空闲内存。所以你可能会在那里引发很多的线程间争用。

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

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

+1

分配器的对象池实现只需要很少的代码更改,而不需要编写分配器ofc。 – 2013-02-26 12:58:08

+1

当然,但很少有比没有更多!我并不是说不使用对象池或其他类型,他们也是一个好主意。 – 2013-02-26 12:59:29

+0

你说得对。使用UINT * nnn = new UINT [5]实现第二​​个变体,并删除与地图和向量具有相同行为的结果。我无法相信我从来没有把新/删除视为争夺资源。我将检查tcmalloc库。 – namezero 2013-02-26 13:19:22