线程分配内存,主进程死掉,会发生什么?

时间:2013-02-13 14:44:20

标签: 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 */
    free(fileName);
    return;
}

此函数驻留在DLL中。临界区和其他内容由一个函数初始化,该函数也驻留在同一个DLL中。

现在,我的主进程(即GUI)有一个Cancel按钮。当用户点击该按钮时,我调用DLL的清理功能,这恰好会破坏我的关键部分。

我发现如果用户在执行thread_func()期间点击取消,thread_func()将继续执行。当它到达临界区代码时,关键部分无效,所以我就在那里退出。这就是我在线程中检查取消事件的方法(因为我的应用程序中没有其他任何东西可以在执行thread_func()期间调用DLL的清理)。

当我发现关键部分无效时,我无法在fileName中释放thread_func()。我的猜测是因为thread_func()因主要进程退出而无法访问fileName。我猜对了吗?我的主要问题是,如果在这种情况下我没有释放fileName,我是否冒着内存泄漏的风险?

我已经搜索了很多相关信息,到目前为止还没有找到任何信息。如果有人能指出我正确的方向/回答我的问题,我会非常高兴。

谢谢!

编辑:

我决定根据kol的建议做一些初步测试(见下面的答案)。我注意到一些非常奇怪的东西,我无法理解。现在我的代码如下:

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()不应该快速回归吗?此外,如果thread_func()threadTerminated,则TRUE内无法进行清理。

这是IMO中最奇怪的部分。当我删除WaitForMultipleObjects()时,我的代码工作得很好!所有清理都会发生,包括thread_func()内的清理。有人可以帮我理解这个吗?

请注意,我现在只在一点检查threadTerminated。我稍后会在其他重点检查。我这样做只是为了看看我是否理解发生了什么。

再次感谢!你的答案非常有帮助。

2 个答案:

答案 0 :(得分:4)

当进程终止时,操作系统将释放它已分配的所有内存,因此不在调用已分配的freefileName不会导致任何问题。

无论如何,我会按以下方式更改代码:

  1. 定义一个标志,指示线程是否应该终止:bool terminated;
  2. 当流程即将终止时,请将terminated设置为truewait for the thread to terminate
  3. 在线程函数中,在重要点检查terminated(例如,在每个循环的条件检查中)。如果terminatedtrue,则停止线程完成的所有操作(例如,停止循环),释放资源(例如,线程分配的空闲内存),然后返回。
  4. 线程终止后(即线程函数返回后),进程可以释放每个剩余的资源(例如,进程分配的空闲内存,删除关键部分等)并退出。
  5. 通过这种方式,您可以避免在线程终止之前删除关键部分,并且可以释放每个已分配的资源。

答案 1 :(得分:1)

  • 你的主题应该有某种形式的循环才有意义。
  • 使用线程时,您需要发明一些方法,以安全,可预测的方式优雅地终止它们。
  • 关键部分是生硬的,用一个线程可以等待的互斥对象替换它们。

设计这个的正确方法是这样的:

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);
  ...
}

编辑:

请记住,创建和删除线程会产生大量开销代码,并可能会降低程序速度。除非它们是工作线程,与开销相比工作量很大,考虑在程序的整个生命周期内保持线程处于活动状态但是处于睡眠状态。