指向 std::thread 的共享指针是一种不好的做法吗?

Posted

技术标签:

【中文标题】指向 std::thread 的共享指针是一种不好的做法吗?【英文标题】:is a shared pointer to std::thread a bad practice? 【发布时间】:2018-05-15 14:34:07 【问题描述】:

使用 std::shared_ptr 有意义吗? 逻辑很简单:如果不需要线程,则删除它,如果需要新线程 - 重新分配它。 有什么方法可以将此概念与池线程进行比较?

我确实知道我系统中线程的确切数量(我开发了图像处理算法,我想给“算法”类的每个子线程一个单独的线程(也许将其设为私有,然后不需要 shared_ptr),这个算法将在哪里运行,如果没有提供图像,则空闲这个私有线程。 这是一个糟糕的概念吗?

【问题讨论】:

恕我直言:除非绝对需要,否则不要使用指针。 也许是***.com/questions/9127816/… ? thread 的共享所有权毫无意义。无论join() 是什么,它也应该拥有它。 @tyker detach() 通常不推荐使用,因为进程终止是残酷且罕见的,并且可能会遇到不可重现的错误,因为它们在被残酷淘汰时正在做一些关键的事情。这是一个经典的错误,不会出现在相对低负载的无负载运行测试中,然后在您不想要它并且有实际工作调试和修复它时在高负载生产中犯规。本质上,每个未同步回来的 detach() 都是竞争条件。我们讨厌那些。 @jameslarge 我想我明白你的意思了。 worker 很可能是std::thread<> 以外的某个类,并且它确实提供了诸如在未来版本中干净地演变为从 1 个线程到多个线程的选项。说V1:在1中将彩色图像处理为灰度。V2:将其切割并并行处理不同的部分。也就是说,紧密耦合的一个优势可能是可靠地实现 25fps。虽然分离是一个很好的目标,但您想为哪种午餐付费仍然是一个问题。所有非平凡的实时 DIP 应用程序都会在某个时候受到性能挑战并最终受到它的约束。 【参考方案1】:

您可能错过了 std::thread 析构函数不会终止线程这一事实。正如 cmets 中已经提到的,if detach or join was not called before, std::thread destructor calls std::terminate。换句话说,std::shared_ptr<std::thread> 很没用。

std::thread 是一个相当低级的对象。你可能想看看:

非阻塞 I/O 线程。 用于阻塞任务和计算的线程池,例如Intel TBB Task Scheduler。 C++17 Extensions for parallel algorithms,或者像 Intel Parallel STL 这样的库。

【讨论】:

您可以在关闭线程之前为join 提供自定义删除器。虽然我认为总体上这不是一个好的设计,但std::shared_ptr<std::thread> 可能有用的少数情况也似乎是这样一个删除器可以工作的情况。 @FrançoisAndrieux 好吧,调用std::thread::join 也不会终止线程。 所以据我了解,系统内部的每个对象最好给私有线程,而不是让它共享?最好的办法是把它汇集起来,不是吗?那么如果我再连接一台相机,我应该动态更改池中的线程数吗? @hagor 这取决于您打算将这些线程用于什么。线程的使用方式有多种。 @MaximEgorushkin 你说得对,我误解了上下文。【参考方案2】:

这个答案在问题中:

给“算法”类的每个孩子一个单独的线程(也许 将其设为私有,则不需要 shared_ptr)

仅在真正共享所有权的情况下使用std::shared_ptr<>

闲置线程通常没有什么害处,但在许多系统上,即使许多线程实例没有运行,也会产生甚至达到上限的开销。

如果存在线程扩散或过多创建和销毁线程和交换的风险,请引入线程池并且仍然不要使用 shared_ptr,因为池拥有线程。

【讨论】:

我不同意泄漏线程对象没有害处。适当的设计不应该泄漏资源。 谢谢您的回复!我用比方说 25 帧专业秒进行图像处理,如果我的算法足够快,我的算法线程会挂起让我们说(1/50 秒)这是资源泄漏。在另一种情况下,假设我的算法比 fps 慢一点,所以我需要多一个线程,这将从池中取出。这是一个很好的设计。问题是:我应该以某种方式更改池中的线程数吗? Re,“仅在真正共享所有权的情况下使用 shared_ptr<>。”我参与过我们决定 all 指针应该是shared_ptr 的项目,无论所有权是否实际共享。原因是简单:只需要​​考虑一种指针语义。 先回答先。在实时图像处理中,您应该尽量避免在设置后更改池大小。使用池的一个原因是创建和终止线程有开销,例如分配一个堆栈供它使用。这可能会在您的动画中显示为滞后。如果分配太多,则会产生大量任务交换开销。分配太少,你没有充分利用。使用en.cppreference.com/w/cpp/thread/thread/hardware_concurrency 来确定那里有什么。您可能应该使用更少的数量,因为您的池不是唯一的过程。你最终会调优。 第二个答案,“仅”使用shared_ptr 并非不合理,但一般建议并非如此。 unique_ptr 在执行中实际上是免费的,而 shared 有开销。但更糟糕的是,您不能(轻松)从shared_ptr 收回所有权,但unique_ptr;;release() 将其归还。这对于与期望所有权的库代码的互操作性可能很重要。我想说两者在现代 C++ 中都如此普遍,以至于混搭不应该真正让任何人感到困惑。事实上,“对象 x 拥有该对象”是最常见的情况,并且具有最简单的语义......

以上是关于指向 std::thread 的共享指针是一种不好的做法吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 std::thread 创建的线程中调用 pthread_sigmask 是一种好习惯吗?

通过指向常量的指针释放内存是一种好习惯吗

C++ 多线程std::thread 详解

C++ 多线程std::thread 详解

传递指向成员函数的指针在 MinGW-w64 中编译,但不在 gcc 中

C++指针