【发布时间】:2016-02-12 01:41:25
【问题描述】:
我有以下函数在使用 _beginthreadex 或 CreateThread 创建的线程上执行:
static volatile LONG g_executedThreads = 0;
void executeThread(int v){
//1. leaks: time_t tt = _time64(NULL);
//2. leaks: FILETIME ft; GetSystemTimeAsFileTime(&ft);
//3. no leak: SYSTEMTIME stm; GetSystemTime(&stm);
InterlockedAdd(&g_executedThreads, 1); // count nr of executions
}
当我取消注释任何行 1.(crt 调用)或 2.(win 32 api 调用)时,线程泄漏并且 _begintreadex 的下一次调用将失败(GetLastError -> 返回错误 (8) -> 没有足够的存储空间来处理这个命令)。 Process Explorer 报告的内存,当 _beginthreadex 开始失败时: 私有 130 Mb,虚拟 150 Mb。
但如果我只取消注释 3. 行(其他 win 32 api 调用),则不会发生泄漏,并且在 100 万个线程后不会失败。这里报告的内存是私有 1.4 Mb,虚拟 25 Mb。而且这个版本运行得非常快(100 万个线程需要 20 秒,而第一个需要 60 秒才能运行 30000 个线程)。
我已经使用 Visual Studio 2013 测试 (see here the test code),编译 x86(调试和发布)并在 Win 8.1 x64 上运行;创建 30000 个线程后,_beginthreadex 开始失败(大多数调用);我想提一下,同时运行的线程在 100 以下。
更新 2:
我对最多 100 个线程的假设是基于控制台输出(计划与完成大致相等)并且线程选项卡中的 Process Explorer 没有报告超过 10 个线程_) 这是控制台输出(没有 WaitForSingleObject,原始代码):
step:0, scheduled:1, completed:1
step:5000, scheduled:5001, completed:5000
...
step:25000, scheduled:25001, completed:24999
step:30000, scheduled:30001, completed:30001
_beginthreadex failed. err(8); errno(12). exiting ...
step:31701, scheduled:31712, completed:31710
rerun loop:
step:0, scheduled:31713, completed:31711
_beginthreadex failed. err(8); errno(12). exiting ...
step:6, scheduled:31719, completed:31716
根据@SHR 和@HarryJohnston 的建议,我一次安排了64 个线程,并等待全部完成,(see updated code here),但行为是相同的。注意我曾经尝试过单线程,但失败是零星的。保留堆栈大小也是 64K! 这是新的日程安排功能:
static unsigned int __stdcall _beginthreadex_wrapper(void *arg) {
executeThread(1);
return 0;
}
const int maxThreadsCount = MAXIMUM_WAIT_OBJECTS;
bool _beginthreadex_factory(int& step) {
DWORD lastError = 0;
HANDLE threads[maxThreadsCount];
int threadsCount = 0;
while (threadsCount < maxThreadsCount){
unsigned int id;
threads[threadsCount] = (HANDLE)_beginthreadex(NULL,
64 * 1024, _beginthreadex_wrapper, NULL, STACK_SIZE_PARAM_IS_A_RESERVATION, &id);
if (threads[threadsCount] == NULL) {
lastError = GetLastError();
break;
}
else threadsCount++;
}
if (threadsCount > 0) {
WaitForMultipleObjects(threadsCount, threads, TRUE, INFINITE);
for (int i = 0; i < threadsCount; i++) CloseHandle(threads[i]);
}
step += threadsCount;
g_scheduledThreads += threadsCount;
if (threadsCount < maxThreadsCount) {
printf(" %03d sec: step:%d, _beginthreadex failed. err(%d); errno(%d). exiting ...\n", getLogTime(), step, lastError, errno);
return false;
}
else return true;
}
这是控制台上打印的内容:
000 sec: step:6400, scheduled:6400, completed:6400
003 sec: step:12800, scheduled:12800, completed:12800
007 sec: step:19200, scheduled:19200, completed:19200
014 sec: step:25600, scheduled:25600, completed:25600
022 sec: step:32000, scheduled:32000, completed:32000
023 sec: step:32358, _beginthreadex failed. err(8); errno(12). exiting ...
sleep 5 seconds
028 sec: step:32358, scheduled:32358, completed:32358
try to create 2 more times
028 sec: step:32361, _beginthreadex failed. err(8); errno(12). exiting ...
032 sec: step:32361, scheduled:32361, completed:32361
rerun loop: 1
036 sec: step:3, _beginthreadex failed. err(8); errno(12). exiting ...
sleep 5 seconds
041 sec: step:3, scheduled:32364, completed:32364
try to create 2 more times
041 sec: step:5, _beginthreadex failed. err(8); errno(12). exiting ...
045 sec: step:5, scheduled:32366, completed:32366
rerun loop: 2
056 sec: step:2, _beginthreadex failed. err(8); errno(12). exiting ...
sleep 5 seconds
061 sec: step:2, scheduled:32368, completed:32368
try to create 2 more times
061 sec: step:4, _beginthreadex failed. err(8); errno(12). exiting ...
065 sec: step:4, scheduled:32370, completed:32370
欢迎任何建议/信息。 谢谢。
【问题讨论】:
-
我不是 Windows 专家,但问题是几乎可以肯定在您未显示的代码中。我看到您已经发布了完整测试程序的链接,但您是否介意首先将该程序缩减为仍然存在内存泄漏的最小可能程序 ,其次,将其编辑到问题中? (除非您在削减过程中发现问题,这会发生。)
-
如果
InterlockedAdd函数可以以任何方式终止线程,那么这可能会导致一些内存泄漏。 -
它不是 Win32 API。这是一个C library function。
-
SHR 是对的。这不是泄漏,您只需要限制线程创建以限制同时运行的线程数。 (您断言一次运行的线程不超过 100 个,但我看不出有任何方法可以证实这一点。)
-
@HarryJohnston 我的断言基于控制台上的打印内容。计划值和完成值差别不大。
标签: c++ multithreading memory-leaks