Windows7内存管理——如何防止并发线程阻塞

Posted

技术标签:

【中文标题】Windows7内存管理——如何防止并发线程阻塞【英文标题】:Windows7 memory management - how to prevent concurrent threads from blocking 【发布时间】:2014-02-12 22:39:14 【问题描述】:

我正在开发一个由两个并发线程组成的程序。一个(这里是“时钟”)定期执行一些计算(10 Hz)并且非常占用内存。另一个(这里是“hugeList”)使用更多的 RAM,但不像第一个那样时间紧迫。所以我决定将其优先级降低到 THREAD_PRIORITY_LOWEST。然而,当线程释放它使用的大部分内存时,关键的内存无法保持其计时。

我能够将问题浓缩为这段代码(确保优化已关闭!): 当 Clock 试图保持 10Hz 的定时,hugeList 线程会分配和释放越来越多的内存,而这些内存不是以任何形式组织的。

#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”方法的第 4 次迭代中,Clock-Thread 受到严重干扰(最多 200 毫秒)。如果 dummyData-vector 在 Clock-Thread 中“请求”一些内存,效果就会消失。

所以,

    有没有什么办法可以提高Win7中Clock-Thread的内存分配优先级? 或者我是否必须将这两个操作拆分到两个上下文(进程)中?

请注意,如果我选择第二个选项,我的原始代码会通过共享变量使用某种 IPC 进行通信。

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

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

编辑

只是想让您知道,到目前为止,我还无法解决我的问题。将代码的两个部分拆分为两个进程似乎不是一种选择,因为我的时间相当有限,而且到目前为止我从未使用过进程。恐怕我无法及时获得正在运行的版本。

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

进一步阅读:

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

【问题讨论】:

怎么样,你知道,使用 free-list 来加速这些分配,是吗? 我注意到您在 wait_ms 中使用了无限循环。您是否尝试过使用 Sleep() ?对 sleep 函数的帮助显示,运行 Sleep() 的线程放弃了其剩余的执行时间,可能会产生一些影响? 如何分析您的代码以找出瓶颈所在? 你们确实意识到我的“在 wait_ms 中的主动等待只是为了模拟我的真实代码正在做什么。那里发生了一些事情。我不能忽略它。当然,另外,分析将显示此 inf 等待循环将需要 99.9% 的 cpu 时间。如果我分析我的原始代码,它总是会卡在已经提到的不同点。 不要分析未优化的代码(也不要检查未优化代码的速度性能)。 【参考方案1】:

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

附言

是的。发布版本重新创建了问题。我用数组替换了前向列表

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;

然后它不会重新创建。 似乎线程调度程序正在惩罚要求大量小内存块。

【讨论】:

谢谢。但是,我无法重现您的结果。在我的机器上,我使用 list 还是 forward_list 都没有关系。另外,即使开启优化也无法解决问题!似乎 vector dummyDate 仍然存在于机器代码中。奇怪... 你到底在使用什么配置?我的代码运行在 Win7, 64bit, VisualStudio2010, i7-3770k, 16GB 在 Win7、64bit、VS2008-32bit、i7-E3-1240、4GB 上调试构建 啊,我明白了。尝试“发布版本”并告诉我为什么这会使事情变慢。至少对我来说是这样。 ://

以上是关于Windows7内存管理——如何防止并发线程阻塞的主要内容,如果未能解决你的问题,请参考以下文章

Java多线程并发09——如何实现线程间与线程内数据共享

redis单线程如何支持高并发

总结--- 知识总结(内存管理线程阻塞GIL锁)

redis怎样解决高并发

网络编程遇到相关问题

redis单线程如何支持高并发