4

我正在研究一个由两个并发线程组成的程序。其中一个(这里是“时钟”)正在定期进行一些计算(10Hz),并且内存密集。另一个(这里是“hugeList”)使用更多的RAM,但不像第一个那么重要。所以我决定把它的优先级降低到THREAD_PRIORITY_LOWEST。然而,当线程释放大部分使用的内存时,关键线程并不能保持其时序。Windows7内存管理 - 如何防止并发线程阻塞

我能够将问题压缩到这一点代码(确保优化已关闭!): while Clock尝试保持10Hz计时hugeList-thread分配并释放越来越多的未组织内存以任何类型的块。

#include "stdafx.h" 
#include <stdio.h> 
#include <forward_list> 
#include <time.h> 
#include <windows.h> 
#include <vector> 

void wait_ms(double _ms) 
{ 
    clock_t endwait; 
    endwait = clock() + _ms * CLOCKS_PER_SEC/1000; 
    while (clock() < endwait) {} // active wait 
} 
void hugeList(void) 
{ 
    SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST); 
    unsigned int loglimit = 3; 
    unsigned int limit = 1000; 
    while(true) 
    { 
     for(signed int cnt=loglimit; cnt>0; cnt--) 
     { 
      printf(" Countdown %d...\n", cnt); 
      wait_ms(1000.0); 
     } 
     printf(" Filling list...\n"); 
     std::forward_list<double> list; 
     for(unsigned int cnt=0; cnt<limit; cnt++) 
      list.push_front(42.0); 
     loglimit++; 
     limit *= 10; 
     printf(" Clearing list...\n"); 
     while(!list.empty()) 
      list.pop_front(); 
    } 
} 
void Clock() 
{ 
    clock_t start = clock()-CLOCKS_PER_SEC*100/1000; 
    while(true) 
    { 
     std::vector<double> dummyData(100000, 42.0); // just get some memory 
     printf("delta: %d ms\n", (clock()-start)*1000/CLOCKS_PER_SEC); 
     start = clock(); 
     wait_ms(100.0); 
    } 
} 

int main() 
{ 
    DWORD dwThreadId; 

    if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&Clock, (LPVOID) NULL, 0, &dwThreadId) == NULL) 
     printf("Thread could not be created"); 
    if (CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)&hugeList, (LPVOID) NULL, 0, &dwThreadId) == NULL) 
     printf("Thread could not be created"); 

    while(true) {;} 
    return 0; 
} 

首先我注意到为链表分配内存要比释放内存快得多。 在我的机器(Windows7)上,在“hugeList”方法的第四次迭代中 - 时钟线程会受到很大的干扰(最多200ms)。如果没有dummyData向量在Clock-Thread中“询问”某些内存,效果就会消失。

所以,

  1. 是否有增加的内存分配的优先级在Win7时钟线程的方法吗?
  2. 或者我必须将两个操作拆分为两个上下文(进程)?

请注意,我的原始代码使用一些通过共享变量的通信,如果我选择了第二个选项,它将需要某种IPC。

请注意,当“hugeList”方法等效于清除boost :: unordered_map并多次输入ntdll.dll!RtIInitializeCriticalSection时,我的原始代码会卡住大约1秒。 (observed by systinernals process explorer)

请注意,观察到的效果不是由于交换,我使用我的16GB(64位win7)1.4GB。

编辑

只是想让你知道,到现在为止我一直没能解决我的问题。将代码的两部分拆分到两个进程似乎不是一种选择,因为我的时间非常有限,而且我从来没有使用过程。恐怕我无法及时得到正在运行的版本。

但是,我设法通过减少非关键线程所做的内存释放次数来降低影响。这是通过使用快速池化内存分配器(如boost库中提供的内存分配器)来实现的。 似乎没有可能在某些不需要同步的threadprivate堆中显式创建某些对象(如我的示例中的巨大转发列表)。

如需进一步阅读:

http://bmagic.sourceforge.net/memalloc.html

Do threads have a distinct heap?

Memory Allocation/Deallocation Bottleneck?

http://software.intel.com/en-us/articles/avoiding-heap-contention-among-threads

http://www.boost.org/doc/libs/1_55_0/libs/pool/doc/html/boost_pool/pool/introduction.html

+0

你知道如何使用* free-list *加快这些分配,是的? –

+0

我注意到你在wait_ms中使用了无限循环。你有没有尝试过使用Sleep()?睡眠函数的帮助表明,运行Sleep()的线程放弃了其余的执行时间,可能会产生一些影响? – Chris

+0

如何分析代码以准确找到瓶颈的位置? –

回答

0

用std :: list替换std :: forward_list,我在corei7 4GB机器上运行你的代码,直到消耗2GB。根本没有干扰。 (在调试版本中)

P.S

是的。发布版本重现了这个问题。我取代转发列表使用数组

double* p = new double[limit]; 
for(unsigned int cnt=0; cnt<limit; cnt++) 
    p[cnt] = 42.0; 

for(unsigned int cnt=0; cnt<limit; cnt++) 
    p[cnt] = -1; 
delete [] p; 

它不会再重现。 看来,线程调度器是要求大量小内存块的惩罚。

+0

谢谢。但是,我无法重现您的结果。在我的机器上,我使用list或forward_list并不重要。另外,即使开启优化也不能解决问题!看起来好像矢量 dummyDate仍然存在于机器代码中。奇怪...
你准确使用什么配置? 我的代码运行在Win7,64位,VisualStudio2010,i7-3770k,16GB – rava

+0

Win7,64bit,VS2008-32bit,i7-E3-1240,4GB – rmi

+0

啊我看。 尝试“发布构建”并告诉我为什么这会让事情变得更慢。至少对我来说它确实如此。 :/ – rava