我在使用_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 call)或 2。(win 32 api call)时,线程泄漏,_begintreadex的下一次调用将失败(GetLastError) - >返回错误(8) - > 没有足够的存储空间可用于处理此命令)。 当_beginthreadex开始失败时,Process Explorer报告的内存: 私人 130 Mb ,虚拟 150 Mb 。
但如果我只取消注释 3。(其他赢得32 api调用)没有发生泄漏,并且在100万个线程之后没有失败。此处记忆的内存为私有 1.4 Mb ,虚拟 25 Mb 。而且这个版本的运行速度非常快(100万个线程的速度为20秒,而第一个版本的速度为300秒的60秒)。
我已经使用Visual Studio 2013测试过(see here the test code),编译了x86(调试和发布)并在Win 8.1 x64上运行;创建30000个线程后_beginthreadex开始失败(大多数调用);我想提一下,同时运行的线程在100以下。
更新2 :
我假设最多100个线程基于控制台输出(预定aprox等于已完成)并且“线程”选项卡中的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
欢迎任何建议/信息。 感谢。
答案 0 :(得分:1)
我猜你弄错了。 看看这段代码:
int thread_func(void* p)
{
Sleep(1000);
return 0;
}
int main()
{
LPTHREAD_START_ROUTINE s = (LPTHREAD_START_ROUTINE)&thread_func;
for(int i=0;i<1000000;i++)
{
DWORD id;
HANDLE h = CreateThread(NULL,0, s,NULL,0,&id);
WaitForSingleObject(h,INFINITE);
}
return 0;
}
泄漏的线程只是因为你调用它而泄漏,所以等待不会改变一个东西,但是当你在性能监视器中看到它时,你会发现所有的线几乎都是不变的。
现在问问自己,当我删除WaitForSingleObject
时会发生什么?
线程的创建比线程运行得快得多,因此您可以达到每个进程的线程限制或每个进程的内存限制。 请注意,如果您正在为x86进行编译,则内存限制为4GB,但只有2GB用于用户模式内存,另外2GB用于内核模式内存。如果你使用默认的堆栈大小(1MB)用于线程,并且程序的其余部分根本不使用内存(它永远不会发生,因为你有代码...),那么你是限制为2000个线程。在2GB完成之后,您无法创建更多线程,直到之前的线程结束。
所以,我的结论是你创建线程并且不等待,并且在一段时间之后,没有留下更多线程的内存。
您可以检查性能监视器是否属于这种情况,并检查每个进程的最大线程数。
答案 1 :(得分:1)
卸载防病毒软件后,无法再现故障(即使代码运行速度与其他方案 3。一样快)。