在各种情况下的线程清理

Thread cleanup during various cases

本文关键字:线程 情况下      更新时间:2023-10-16

我是新线程编程。

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

当线程在执行特定代码段后正常返回时会发生什么情况?如何在c++中清理本地堆栈数据或动态分配的数据?我们是否也为此目的维护清理处理程序?

我正在使用posix线程。

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

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

如果线程正常执行,其间没有退出或取消,会发生什么情况?

谢谢。

首先你用的是c++ 11吗?

如果您正在使用它,请查看std::thread的文档和那里的示例。如果您正在使用c++新线程工具,那么处理程序就像任何其他c++对象一样。如果你把它们作为对象销毁,那么清理工作就会正确完成。

如果你不能使用c++ 11或想要使用其他库,那么你应该指定,并且你的最佳实践可能是将它们封装到一个"对象管理器"中,并从那里管理内存。

编辑:好吧,说到pthreads。但是如果您计划使用pthread_cleanup之类的东西,那么您可以放弃c++。c++背后的思想(其中之一)是封装和干净的内存管理。如果您计划手动使用C中使用的钩子和回调,那么就这样做吧。但我不建议这么做。

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

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

在线程取消或线程退出的情况下,我可以注册一个函数,其中可以使用pthread_cleanup_push和pthread_cleanup_pop电话。

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

  1. 真的不要这样做
  2. 在任何情况下都不会在惯用的c++中这样做,因为它不会与堆栈展开交互(也就是说,你的智能指针或局部作用域对象都不会被销毁)。
  3. 有一些情况下,它可能是好的,但很难做正确的
  4. 如果你问这个问题,你可能不在这些情况之一
  5. 我不是在开玩笑,不要用pthread_cancel

如果线程正常执行,其间没有退出或取消,会发生什么情况?

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


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

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

如果你使用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++的规则。

pthreads库(不确定这是否是您正在使用的)具有非常简单的清理处理程序方法。如果需要的话,有几种方法可以注册清除处理程序。请看pthread_cleanup_push的手册页。

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

可以叫我保守,但我从不使用官方的线程停止方法(例如pthread_cancel())。通常程序中有几个点,如果线程在其中一个点上停止,那么清理这些点是非常困难的。

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

  close(fd);
  fd = -1;

关闭fd的清理代码应该是:

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

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

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