由于新的const,C++ 11对象在多线程环境中是否可能更慢?

Ale*_*lex 4 c++ multithreading const thread-safety c++11

根据Herb Sutter(http://isocpp.org/blog/2012/12/you-dont-know-const-and-mutable-herb-sutter),在C++ 11中,const方法不能改变对象位 -明智的,或者如果它们具有可变数据成员,则必须执行内部同步(例如,使用互斥锁).

假设我有一个全局对象,我从多个线程访问,并假设它有可变成员.为了论证,让我们假设我们不能修改类的来源(它由第三方提供).

在C++ 98中,这些线程将使用全局互斥锁来同步对此对象的访问.因此,访问需要单个互斥锁定/解锁.

但是,在C++ 11中,对此对象的任何const成员函数调用也将调用内部同步,因此,对此对象进行单个const函数调用将花费2次锁定/解锁操作(或更多,具体取决于函数的数量)你从一个线程打电话).请注意,仍然需要全局互斥锁,因为const似乎对编写器没有任何作用(除非其中一个非const方法调用const方法,否则可能会减慢它们的速度).

所以,我的问题是:如果我们所有的类都必须在C++中这样(至少可以被STL使用),这是否会导致过度的同步措施?

谢谢

编辑:一些澄清:

  1. 似乎在C++ 11中,除非其const成员函数在内部同步(或不执行任何写入),否则不能将类与标准库一起使用.

  2. 虽然C++ 11本身不会自动添加任何同步代码,但是符合标准库的类不需要在C++ 98中进行同步,但在C++ 11中需要它.因此,在C++ 98中,您可以避免对可变成员进行任何内部同步,但在C++ 11中则不能.

Jon*_*ely 10

在C++ 11中,对该对象的任何const成员函数调用也将调用内部同步

为什么?这种同步不只是神奇地出现在课堂上,只有在有人明确添加时才会出现.

因此,对此对象进行单个const函数调用将花费2次锁定/解锁操作

只有当某人添加了一个内部互斥锁并且你还使用了一个外部互斥锁...但是为什么你会这样做呢?

请注意,仍然需要全局互斥锁,因为const似乎对编写器没有任何作用(除非其中一个非const方法调用const方法,否则可能会减慢它们的速度).

如果类具有用于使const成员线程安全的内部互斥锁,那么它也可以用于非const成员.如果类没有内部互斥,那么情况与C++ 98相同.

我认为你看到的是一个不存在的问题.

Herb的"const的新含义"不是由语言或编译器强制执行的,它只是设计指导,即良好代码的习惯用语.要遵循该指导,您不要向每个类添加互斥锁,以便const允许mutable成员修改成员,避免使用可变成员! 在绝对必须拥有可变成员的极少数情况下,要么要求用户自己进行锁定(并明确将该类记录为需要外部同步),要么添加内部同步并支付额外费用......但这些情况应该很少见,因此,"C++ 11对象由于新的const而变慢"并非如此,因为大多数精心设计的对象无论如何都没有可变成员.

  • 它**不是**这样说的.标准库中的标准promises类符合这些要求(但是**非常**标准库中的几个类具有可变成员或非const静态成员或独立对象之间共享的其他"非本地"数据)但它没有需要任何用户定义的类型.如果这对你造成了问题,那么你做错了:要么你有太多`mutable`成员,要么你在线程之间共享太多对象.停止这样做,生活更轻松. (6认同)
  • @Alex:如果你的类有很多可变成员,那么你应该得到破解的代码.如果您的类没有可变成员,那么您的代码将不需要修改.所以我在这里看不到问题. (5认同)
  • _"如果你想将这些类与标准库一起使用,则需要它"_为什么?仅当您希望以涉及多个线程的并发,非同步访问的方式使用它们时.我这里没有看到问题. (2认同)

Nic*_*las 6

是的,你是绝对正确的.您应该使对象遵循这些准则,因此在C++ 11中访问它们可能会更慢.如果且仅当:

  1. 该类具有mutable成员const函数修改的成员.

  2. 正在从多个线程访问该对象.

如果你确保其中至少有一个是不真实的,那么没有任何改变.从多个线程访问的对象数应始终尽可能小.并且具有可变成员的类的数量应该是最小的.所以你在谈论一组最小的对象.

即便如此......所需要的只是数据竞赛不会被打破.根据可变数据的含义,这可能只是一个原子访问.

我没有在这里看到问题.很少有标准库对象具有可变成员.我也不怕你找到一个合理的实现basic_string,vector,map等需要可变成员.

似乎在C++ 11中,除非其const成员函数在内部同步(或不执行任何写入),否则不能将类与标准库一起使用.

这是不正确的.你当然可以.你不能做的是尝试以对那些可变成员"执行任何写入"的方式跨多个线程访问该类.如果您从未以特定方式跨线程通过该C++ 11类访问该对象,那么您没问题.

所以是的,你可以使用它们.但是你只能得到自己班级提供的保证.如果你以不合理的方式通过标准库类使用你的类(比如你的const成员函数没有const或正确同步),那那就是你的错,而不是库的.

因此,在C++ 98中,您可以避免对可变成员进行任何内部同步,但在C++ 11中则不能.

这就像说你可以逃离罗马帝国的计算机犯罪.当然可以.他们当时没有电脑 ; 所以他们不知道计算机犯罪什么.

C++ 98/03没有"线程化"的概念.因此,标准没有"内部同步"的概念,所以你能够或不能"逃避"既没有定义也没有定义.提出标准问题比询问Ceaser当天的黑客法律是没有意义的.

现在C++ 11实际上定义了这个概念和竞争条件的概念,C++ 11能够说明何时可以"逃避不进行任何内部同步".

或者,换句话说,这就是两个标准如何回答你的问题:mutable当通过const标准库中声明的成员函数访问成员时,潜在的数据竞争结果是什么?

C++ 11:当const函数访问时,任何内部成员都不会有数据争用.此类函数的所有标准库实现必须以不会发生数据争用的方式实现.

C++ 98/03:什么是数据竞争?

  • 这是一个比我更好的答案,并将我在几条评论中提出的各种观点汇总到一个连贯的答案中,并做了一个很好的类比 - 很好地完成了. (2认同)