C 线程分配内存,主进程死亡,会发生什么?
我目前正在检查一些代码的内存泄漏,这种可能性让我震惊。基本上我所做的伪代码如下C 线程分配内存,主进程死亡,会发生什么?,c,windows,memory-leaks,C,Windows,Memory Leaks,我目前正在检查一些代码的内存泄漏,这种可能性让我震惊。基本上我所做的伪代码如下 void thread_func() { char *fileName = malloc(someSize); /* Do something with fileName and other things */ /* Enter a critical section */ modify some global variables /*Exit critical section
void thread_func()
{
char *fileName = malloc(someSize);
/* Do something with fileName and other things */
/* Enter a critical section */
modify some global variables
/*Exit critical section */
free(fileName);
return;
}
此函数驻留在DLL中。关键部分和其他内容由同样驻留在同一DLL中的函数初始化
现在,我的主进程(GUI)有一个取消按钮。当用户单击该按钮时,我调用DLL的清理函数,该函数恰好破坏了我的关键部分
我发现,如果用户在执行thread\u func()
期间单击取消,thread\u func()
将继续执行。当它到达临界区代码时,临界区无效,所以我就从那里退出。这就是我在线程内检查cancel事件的方式(因为在执行thread\u func()
的过程中,我的应用程序中没有其他东西可以调用DLL的清理)
当我发现关键部分无效时,我无法在thread\u func()
中释放fileName
。我猜是因为自从主进程退出后,thread\u func()
已经失去了对fileName
的访问权。我猜对了吗?我的主要问题是,在这种情况下,如果我不释放fileName
,我是否有内存泄漏的风险
我已经搜索了很多相关信息,到目前为止还没有找到任何东西。如果有人能给我指出正确的方向/回答我的问题,我会非常高兴
谢谢
编辑:
我决定根据科尔的建议做一些初步测试(见下面的答案)。我注意到一件非常奇怪的事情,我就是不明白。现在我的代码如下:
void thread_func()
{
char *fileName = malloc(someSize);
/* Do something with fileName and other things */
if(threadTerminated)
{
/* Cleanup */
return;
}
/* Enter a critical section */
modify some global variables
/*Exit critical section */
free(fileName);
return;
}
在我的GUI中,OnCancel事件处理程序类似于:
void OnCancel()
{
threadTerminated = TRUE;
WaitForMultipleObjects(noOfRunningThreads, threadHandles, TRUE, INFINITE);
/* Other cleanup code */
}
我注意到WaitForMultipleObjects()
无限期挂起,我的GUI变得没有响应。WaitForMultipleObjects()
不应该快速返回吗?另外,如果threadTerminated
为TRUE
,则不会在thread\u func()中进行任何清理
这是我最奇怪的部分。当我删除WaitForMultipleObjects()
时,我的代码工作得很好!所有清理都会发生,包括thread\u func()
内的清理。有人能帮我理解吗
请注意,我现在只在一个点上检查threadTerminated
。稍后,我将在其他重要方面进行检查。我这么做只是想看看我是否明白发生了什么
再次感谢!您的回答非常有用。当进程终止时,操作系统将释放其分配的所有内存,因此不调用分配的文件名上的free
不会导致任何问题
无论如何,我会按以下方式更改代码:
定义一个指示线程是否应该终止的标志:bool terminated代码>
当进程即将终止时,将终止
设置为真
,然后李>
在thread函数中,在重要点(例如,在每个循环的条件检查中)检查terminated
。如果terminated
为true
,则停止线程执行的所有操作(例如,停止循环),释放资源(例如,线程分配的空闲内存),然后返回
线程终止后(即线程函数返回后),进程可以释放所有剩余资源(例如,进程分配的空闲内存、删除关键部分等)并退出
通过这种方式,您可以避免在线程终止之前删除关键部分,并且可以释放所有分配的资源。
- 你的线程应该有某种形式的循环才有意义
- 在处理线程时,您需要发明一些方法,以安全、可预测的方式优雅地终止它们
- 关键部分是钝的,用线程可以等待的互斥对象替换它们
正确的设计方法如下:
HANDLE h_event_killthread = CreateEvent(...);
HANDLE h_mutex = CreateMutex(...);
...
void thread_func()
{
const HANDLE h_array [] =
{
h_event_killthread,
h_mutex
};
... // malloc etc
bool time_to_die = false;
while(!time_to_die)
{
DWORD wait_result;
wait_result = WaitForMultipleObjects(2, // wait for 2 handles
h_array, // in this array
FALSE, // wait for any handle
INFINITE); // wait forever
if(wait_result == WAIT_OBJECT_0) // h_event_killthread
{
time_to_die = true;
}
else if(wait_result == (WAIT_OBJECT_0+1)) //h_mutex
{
// we have the mutex
// modify globals here
ReleaseMutex(h_mutex);
// do any other work that needs to be done, if meaningful
}
}
cleanup();
}
// and then in the GUI:
void cancel_button ()
{
...
SetEvent(h_event_killthread);
WaitForSingleObject(the_thread, INFINITE);
...
}
编辑:
请记住,创建和删除线程会产生大量开销代码,可能会减慢程序的速度。除非它们是工作线程,其中工作量与开销相比是重要的,考虑在程序的整个生命周期内保持线程存活,但睡眠。如果泄漏没有增长,通常是一个不便,而不是一个实际问题,因为程序无论如何都会终止,并且泄漏不会随着时间的推移而增长。@claptrap,说得好!但是我想删除我发现的任何内存泄漏情况。如果仅在进程退出时才调用DLL的清理函数,只需不破坏关键部分即可。这座建筑正在被拆除,所以你不需要扫地!如果需要处理DLL正在卸载但进程将继续进行的情况,则需要根据现有答案将清理与线程同步。请记住,即使没有关键部分,当线程仍在运行DLL中的代码时,也无法安全地卸载DLL。我不建议使用bool。改为使用事件,这样线程就可以在不产生无意义CPU负载的情况下休眠。看看我的答案。@Lundin我不确定一个活动是否更适合这个目的。我的意思是没有必要在这面旗帜上等待。线程执行它的任务,并在决定点检查标志,以决定它是否可以继续或应该以一种优雅的方式终止。(Delphi的设计者使用了相同的逻辑:TThread类有一个Terminated属性,它是一个布尔值。)谢谢你的回答!这显然是解决我问题的可能办法@Lundin,你为什么不推荐一个bool?只是好奇。很难说会是什么