4

根据 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 中则不能。

4

2 回答 2

10

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

为什么?这种同步不仅仅神奇地出现在类中,它只有在有人明确添加时才会出现。

因此,对该对象的单个 const 函数调用可能会花费 2 次锁定/解锁操作

仅当有人向其添加了内部互斥体并且您还使用了外部互斥体时……但是您到底为什么要这样做?

请注意,仍然需要全局互斥锁,因为 const 似乎对编写器没有任何作用(如果非 const 方法之一调用 const 方法,可能还会减慢它们的速度)。

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

我认为你看到了一个不存在的问题。

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

于 2013-06-07T20:48:40.840 回答
6

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

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

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

如果您确保其中至少有一个是不真实的,那么什么都不会改变。从多个线程访问的对象数量应始终尽可能少。并且具有可变成员的类的数量应该最少。所以你说的是一组最小对象的最小集合。

即便如此……所需要的只是数据竞赛不会被打破。根据可变数据甚至是什么,这可能只是一个原子访问。

我看不到这里的问题。很少有标准库对象具有可变成员。我挑战你找到需要可变成员的 , , 等basic_stringvector合理实现。map

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

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

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

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

这就像说您可以在罗马帝国摆脱计算机犯罪。当然可以。那时他们没有电脑。所以他们不知道什么是计算机犯罪

C++98/03 没有“线程”的概念。因此,该标准没有“内部同步”的概念,因此您可以或不能“摆脱”的内容既没有定义也没有定义。问这个标准问题比问凯瑟时代的黑客法律是什么更有意义。

既然 C++11 实际上定义了这个概念和竞争条件的概念,C++11 就可以说你什么时候可以“不做任何内部同步”。

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

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

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

于 2013-06-07T21:58:28.823 回答