用c ++ 11等效项替换boost :: thread和boost :: mutex是否明智?


153

动机:我正在考虑的原因是,我的天才项目经理认为boost是另一种依赖,并且它很可怕,因为“您依赖它”(我试图解释boost的质量,然后过了一段时间就放弃了:( )。我之所以愿意这样做的较小原因是我想学习c ++ 11的功能,因为人们将开始在其中编写代码。

  1. #include<thread> #include<mutex>和Boost等价物之间是否存在1:1映射?
  2. 您是否认为用c ++ 11的
    东西代替boost的东西是个好主意?我的用法是原始的,但是有没有一些示例说明std不提供什么功能呢?或(亵渎)反之亦然?

PS我使用GCC,所以标题在那里。


46
IMO Google编码准则在许多方面都很愚蠢。他们不允许从C ++ 11开始自动运行... :)
NoSenseEtAl 2012年

5
引用准则:[自动]会影响可读性[因为它会删除]已检查的冗余(例如类型名称),可能会对读者有所帮助。
Andrew Tomazos 2012年

31
for(auto it = v.begin()... :)
NoSenseEtAl 2012年

15
@ AndrewTomazos-Fathomling:真的吗?就我个人而言,我认为我从来没有关心过迭代器的实际类型(可能是几次),仅关心受支持的操作……我认为语法冗余很少是一个好主意(DRY)。
灰熊2012年

16
顺便说一句,谷歌修改了它的愚蠢的指导方针,所以现在终于他们允许自动
NoSenseEtAl 2013年

Answers:


192

Boost.Thread和C ++ 11标准线程库之间有一些区别:

  • Boost支持线程取消,C ++ 11线程不支持
  • C ++ 11支持std::async,但Boost不支持
  • Boost具有boost::shared_mutex用于多读者/单作家的锁定。类似的std::shared_timed_mutex仅可自C ++ 14(N3891),而std::shared_mutex仅可自C ++ 17(N4508)。
  • C ++ 11超时与Boost超时不同(尽管现在应该很快改变Boost.Chrono了)。
  • 一些名称是不同的(例如boost::unique_futurevs std::future
  • 的参数传递语义std::thread不同于boost::thread--- Boost使用boost::bind,后者需要可复制的参数。std::thread允许将仅移动类型std::unique_ptr作为参数传递。由于使用boost::bind,占位符的语义(例如_1嵌套绑定表达式中的语义)也可以不同。
  • 如果您未明确调用join()detach()boost::thread析构函数和赋值运算符将调用detach()要销毁/分配给的线程对象。对于C ++ 11 std::thread对象,这将导致std::terminate()对应用程序的调用并中止该应用程序。

为了阐明仅移动参数的要点,以下是有效的C ++ 11,并将所有权int从临时std::unique_ptr转移到f1新线程启动时的参数。但是,如果您使用boost::thread它,则它将无法使用,因为它在boost::bind内部使用,并且std::unique_ptr无法复制。GCC随附的C ++ 11线程库中还有一个错误阻止了此工作,因为该错误std::bind也在该实现中使用。

void f1(std::unique_ptr<int>);
std::thread t1(f1,std::unique_ptr<int>(new int(42)));

如果您使用的是Boost,那么如果您的编译器支持的话,您可能可以相对轻松地切换到C ++ 11线程(例如,Linux上的最新版本的GCC具有可在-std=c++0x模式下使用的C ++ 11线程库的最完整实现)。

如果您的编译器不支持C ++ 11线程,那么您可能能够获得第三方实现,例如Just :: Thread,但这仍然是一个依赖项。


1
有单独的锁定/解锁方法读取器和写入(lock/ unlock作家与“lock_shared / unlock_shared”的阅读器)。只要没有编写者正在使用它,多个读取者就可以调用lock_shared而不会阻塞。
戴夫S

2
shared_mutex文档位于 boost.org/doc/libs/1_47_0/doc/html/thread/…。您可以将互斥锁锁定为共享或互斥,然后使用相应的解锁功能。您也可以使用RAII类型来做到这一点(shared_lock需要一个共享读锁,lock_guardunique_lock采取排它锁)。我试图阐明仅移动类型的意义。
安东尼·威廉姆斯

3
让我震惊的另一件事:在boost中,正在运行的线程的析构函数将其分离(boost.org/doc/libs/1_47_0/doc/html/thread/…),而在C ++中,正在运行的线程的析构函数终止()(FDIS 30.3.1.3)
Cubbi

3
在C ++ 11中,try_scoped_lock功能涵盖在中std::unique_lock。有一个使用互斥量和的构造函数std::try_to_lock,然后将调用try_lock()互斥量而不是lock()。参见stdthread.co.uk/doc/headers/mutex/unique_lock/…–
Anthony Williams

4
是的,自从我写这篇文章以来,Boost.Thread已经越来越接近C ++ 11标准,这主要是由于Vicente Botet的工作。
安东尼·威廉姆斯

24

std::thread基本上是模仿的boost::thread,有一些区别

  • boost的不可复制的,一对一映射到一个OS线程的语义得以保留。但是该螺纹是可移动的,以允许从工厂功能返回螺纹并将其放入容器中。
  • 该提议为加上了取消功能boost::thread,这是一个很大的麻烦。此更改不仅对线程有很大影响,而且对C ++线程库的其余部分也有很大影响。据信,这种巨大的改变是有好处的,这是合理的。
    • 现在,线程析构函数必须在分离之前调用cancel,以避免在取消父线程时意外泄漏子线程。
    • 现在需要一个显式的分离成员来启用分离而不取消。
  • 线程句柄和线程标识的概念已分为两类(它们在中是同一类boost::thread)。这是为了支持更容易地操作和存储线程标识。
  • 添加了创建线程ID的能力,该线程ID保证不与其他可连接线程进行比较(boost::thread不具有此功能)。这对于想要知道它是否由与先前调用相同的线程执行的代码非常方便(递归互斥是一个具体的示例)。
  • 存在一个“后门”来获取本机线程句柄,以便客户端可以根据需要使用基础操作系统来操纵线程。

这是从2007年,因此一些点不再有效:boost::threadnative_handle功能,并且正如评论者所指出的,std::thread不再具有取消功能。

我找不到boost::mutex和之间的任何重大差异std::mutex


2
std::thread没有取消;就是boost::thread这样!
安东尼·威廉姆斯,

@Anthony您确定您不是要interrupt()使用boost :: thread吗?此外,这似乎是一项原始提案,自2007
Alex B

4
是的,取消升压称为“中断”。是的,这是一个旧建议。C ++ 11标准的最新公共草案(包括线程库)是open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf
Anthony Williams

6

不迁移到的原因有一个std::thread

如果您使用的是静态链接,std::thread则由于以下gcc错误/功能而变得不可用:

即,如果您调用std::thread::detachstd::thread::join它将导致异常或崩溃,而boost::thread在这些情况下可以正常工作。


我看到一个错误是未确认的,另一个错误是无效的,并有一条评论说,记者应该针对libpthread.a。您完全确定自己在说什么吗?
einpoklum 2015年

1
@einpoklum,您应该可以通过使用使其工作Wl,--whole-archive -lpthread -Wl,--no-whole-archive,例如,请参见以下答案stackoverflow.com/a/23504509/72178。但这并不是很简单的链接方式libpthread.a,也被认为是坏主意。
ks1322

4
我们可以假设这些错误已在2016年被修复吗?该错误于2012年发布,来自gcc 4.9.2,它正式支持C ++ 11,因此我们在正式支持之前不能抱怨C ++ 11。
飞溅

6

企业案例

如果您正在为需要在中型到大型操作系统上运行的企业编写软件,并因此在这些操作系统上使用各种编译器和编译器版本(尤其是相对较旧的版本)进行构建,则我的建议是远离现在完全是C ++ 11。这意味着您不能使用std::thread,我建议使用boost::thread

基本/技术启动案例

如果您正在为一个或两个操作系统编写程序,那么您肯定会知道您只需要使用主要支持C ++ 11(例如VS2015,GCC 5.3,Xcode 7)的现代编译器进行构建,而您还没有取决于boost库,那么std::thread可能是一个不错的选择。

我的经验

我个人偏爱强化,频繁使用,高度兼容,高度一致的库,例如boost与非常现代的替代品。对于诸如线程之类的复杂编程主题尤其如此。另外,我长期以来boost::thread在各种各样的环境,编译器,线程模型等方面都取得了巨大的成功(总体而言是boost)。当我选择它时,我选择boost。


1
@UmNyobe他是对的。C ++ 11线程的许多实现都被破坏了,我什至惊讶于人们甚至考虑使用它。
StaceyGirl '18 -4-9


1

关于在C ++ 17中添加的std :: shared_mutex

这里的其他答案很好地概述了总体差异。但是,std::shared_mutex该提升解决了几个问题。

  1. 可升级的mutices。这些是缺少的std::thread。它们使读者可以升级为作家,而没有其他作家进入您的视野。这些使您可以执行一些操作,例如在读取模式下对大型计算进行预处理(例如,对数据结构重新索引),然后升级为写入以应用重新索引,同时仅保持写入锁定时间较短。

  2. 公平。如果您使用不断进行阅读活动std::shared_mutex,则您的作家将被无限期软锁定。这是因为,如果有其他读者出现,他们将始终获得优先权。使用boost:shared_mutex,所有线程最终将被赋予优先级。(1)读者和作家都不会饿死。

tl; dr的意思是,如果您拥有一个高吞吐量的系统而没有停机时间和非常高的竞争,std::shared_mutex那么如果不手动在其上构建优先级系统,该系统将永远无法为您服务。boost::shared_mutex将开箱即用,尽管在某些情况下您可能需要进行修补。我认为std::shared_mutex的行为是一个潜在的错误,等待大多数使用它的代码发生。

(1) 它使用实际算法基于OS线程调度程序。以我的经验,当读取饱和时,与OSX / Linux相比,Windows上的暂停(获得写锁定时)更长。


0

我试图使用std的shared_ptr而不是boost,但实际上在此类的gcc实现中发现了一个错误。我的应用程序由于两次调用析构函数而崩溃(此类应该是线程安全的,并且不会产生此类问题)。移动到boost :: shared_ptr之后,所有问题都消失了。C ++ 11的当前实现仍不成熟。

Boost还具有更多功能。例如,std版本中的标头不提供流的序列化程序(即cout <<持续时间)。Boost有许多使用自己的等价库的库,但不与std版本配合使用。

综上所述-如果您已经有一个使用boost编写的应用程序,那么将代码保持原样会更安全,而不是花一些心思转向C ++ 11标准。


4
shared_ptr析构函数不必是线程安全的,它的不确定的行为有一个线程访问对象,而另一个线程正在摧毁它。如果您认为自己在GCC的shared_ptr中发现了一个错误,请报告该错误,否则,就您使用错误为准。
Jonathan Wakely
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.