各种情况下的线程清理

时间:2014-08-06 13:06:41

标签: c++ multithreading

我是新的线程编程。

我被告知必须维护清理处理程序来清除动态分配的线程特定数据或线程特定的堆栈数据,当线程被取消或退出而没有完全执行特定代码时。

在执行特定代码段后正常返回线程的情况如何。本地堆栈数据或动态分配的数据如何在C ++中清理?我们是否也为此保留了清理处理程序?

我正在使用posix线程。

假设我有一个函数将由不同的线程调用,并且在函数中我已动态分配数据。

在线程取消或线程退出的情况下,我可以使用pthread_cleanup_push和pthread_cleanup_pop调用注册一个可以完成释放的函数。

如果线程正确执行而没有任何退出或取消,则会出现什么情况。

感谢。

5 个答案:

答案 0 :(得分:3)

首先,您使用的是C ++ 11吗?

如果您正在使用它,请查看std::thread的文档及其中的示例。如果您正在使用C ++新线程工具,那么处理程序就像任何其他C ++对象一样。如果将它们作为对象正确销毁,则可以正确完成清理。

如果您不能使用C ++ 11或想要使用其他库,那么您应该指定并且可能最佳做法是将它们封装到对象管理器中。并从那里管理记忆。

编辑:好的,谈论pthreads。但是如果你打算使用pthread_cleanup等等,你可以放弃C ++。 C ++背后的想法(其中之一)是封装和清理内存管理。如果您计划手动使用C中使用的挂钩和回调,则执行此操作。但我不建议。

答案 1 :(得分:2)

  

假设我有一个将被不同线程调用的函数   在我已动态分配数据的函数中。

行。由于您正在编写C ++,所有这些动态分配的数据都将由智能指针拥有,对吧?

  

在线程取消或线程退出的情况下,我可以注册一个   使用pthread_cleanup_push和可以完成释放的函数   pthread_cleanup_pop调用。

可以这样做,但可能永远不应该这样做。正确使用pthread_cancel的规则是:

  1. 真的不这样做
  2. 在任何情况下都不会在惯用的C ++中执行此操作,因为它不会与堆栈展开交互(即,您的智能指针或本地范围对象都不会被销毁)。
  3. 在某些情况下,它可能没问题,但很难正确执行
  4. 如果你问这个问题,你可能不会处于其中一种情况
  5. 我不是在开玩笑,不要使用pthread_cancel
  6.   

    如果线程正确执行而没有任何退出或取消,则会出现什么情况。

    如果已经编写了pthread_cleanup_push个处理程序,只需在适当的位置调用pthread_cleanup_pop,然后传递非零execute个参数。这完全等同于C ++对象在超出范围时执行其析构函数的方式,无论是因为块正常结束还是抛出了异常。


    NB。如果您正在使用GCC(测试显示Jonathan Wakely是正确的,这真的让我感到惊讶),上述警告被夸大了。如果您是或可能使用不同的编译器或pthread实现,那么您应该远离并实现自己的机制。

答案 2 :(得分:1)

pthreads库(不确定这是否是您正在使用的)具有非常简单的清理处理程序方法。如果需要,有一些方法可以注册清理处理程序。看看this man page for pthread_cleanup_push

然而,只要你没有任何内存泄漏或任何只是返回线程通常应该没问题。

答案 3 :(得分:1)

  

我被告知必须维护清理处理程序来清除动态分配的线程特定数据或线程特定的堆栈数据,当线程被取消或退出而没有完全执行特定代码时。

如果使用pthread_setspecific创建特定于线程的数据,那么在调用pthread_key_create时可以提供“析构函数”(与C ++析构函数不同),并且在运行时运行析构函数线程退出。

我不知道“特定于线程的堆栈数据”是什么意思。您无需执行任何操作来清理堆栈数据。

  

在执行特定代码段后正常返回线程的情况如何。本地堆栈数据或动态分配的数据如何在C ++中清理?我们是否也为此保留了清理处理程序?

当线程退出(正常或异常)时,将运行特定于线程的数据的析构函数。在C ++ 11中,将运行thread_local变量的析构函数。

如果退出函数然后应用C ++的常规规则,则会为堆栈对象运行析构函数。动态分配的资源不会自动释放,因此您应该使用RAII。在这方面使用线程不会改变任何东西,C ++函数仍然是C ++函数。

  

在线程取消或线程退出的情况下,我可以使用pthread_cleanup_push和pthread_cleanup_pop调用注册一个可以完成释放的函数。

如果您正在使用GCC和GNU libc,则通过抛出一个特殊的异常类型来实现线程取消,这将导致析构函数运行。因此,如果您使用RAII编写好的,干净的C ++,那么一切正常,您不需要使用pthread_cleanup_push。但这不便携。

  

如果线程正确执行而没有任何退出或取消,则会出现什么情况。

然后你的函数正常返回,析构函数当然正常运行。使用线程不会突然改变C ++的规则。

答案 4 :(得分:0)

叫我保守,但我从不使用官方线程停止方法(例如pthread_cancel())。通常程序中有几个点,如果线程停在其中一个点上,则极难清理。

只是一个简单的例子:我通常在关闭它们之后将文件描述符设置为-1。

  close(fd);
  fd = -1;

关闭fd的清理代码应为:

 if (fd != -1) close(fd);

但是,如果在close(fd)之前fd = -1之后停止,则清理代码会失败(好吧,执行close(-1)不是一个大错误,但你很容易想象更糟糕的情况)。

所以,我使用"停止请求" 标志为线程,在线程启动时设置为false,并设置为{{ 1}}由另一个(控制器,主)线程。线程本身会定期检查它,如果它true,则执行清理并退出。因为"断点"以线程着称,清理不是问题。