Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 线程分配内存,主进程死亡,会发生什么?_C_Windows_Memory Leaks - Fatal编程技术网

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?只是好奇。很难说会是什么