访问器中的 QReadWriteLock

QReadWriteLock in accessors

我有一个 MyClass 的 QList。从列表中添加和删除项目很少见,并且由公共列表互斥锁控制。 MyClass包含几个子结构和个人QReadWriteLock:

MyClass{
private:
   Substructure substructure;
   QReadWriteLock rwlElem;
}

我把储物柜放在这样的配件中:

Substructure MyClass::getSub(){
  QReadLocker lock(&rwlElem);
  return substructure;
}

我希望子结构的副本能够安全返回。和我在 setter 里做的一样。这样使用储物柜是好习惯吗?首先发生什么:复制建造子结构或破坏储物柜?

我先回答你的最后一个问题:

What happens first: copy construction of the substructure or destruction of the locker?

TL;DR: 复制构造发生在所有局部变量的破坏之前。

这已在 Whosebug 上多次被问到,请参阅 here, and here

CWG 1885 已将更精确的措辞应用于 C++14 标准:

N4606 §6.6.3 [stmt.return]/3

The copy-initialization of the result of the call is sequenced before the destruction of temporaries at the end of the full-expression established by the operand of the return statement, which, in turn, is sequenced before the destruction of local variables (6.6) of the block enclosing the return statement.


I have a QList of MyClass. Appending and deleting items from list are rare and cotrolled by common list mutex. MyClass contains several substructures and personal QReadWriteLock

要锁定两个互斥量会使您容易死锁。但是,您可以通过遵循以下两个准则之一来避免这种情况:

  • 避免嵌套锁,一次只锁定一个互斥体。确保在获取新锁之前释放当前持有的锁。
  • 如果这对您来说不可能,您必须始终以固定顺序获取锁,并且此顺序在整个应用程序中应该保持一致(即始终锁定当您需要同时锁定两个互斥锁时,在锁定特定 MyClass 实例的 read/write 互斥锁之前保护整个列表的互斥锁。

Same things I made in setters. Is this good practice to use lockers like that?

假设您在 setter 中使用了 QWriteLocker,这应该没问题(从某种意义上说,它不会导致未定义的行为)。但是你必须注意你的界面真正在说什么。例如,当您对类型 MyClass:

的实例 mc 执行类似操作时
Substructure s = mc.getSub();
if(s == anotherSubstructure)
    mc.doSomething();

当执行上述代码的线程将 sanotherSubstructure 进行比较时,另一个线程可能会更改 mc 中的实际 substructure,并且此更改对前一个线程不可见(因为它有自己的 substructure 副本)。

重点是,当您执行 Substructure s = mc.getSub(); 时,您唯一知道的是 mc 的子结构在某些时候等于 s 时间点 ,一旦您释放 getSub 函数中的锁定,此值可能会立即更改。

在我上面的例子中,doSomething() 不能假定 mc 的子结构的值仍然等于 anotherSubstructure。您可能需要将需要原子化的操作分组到它们自己的函数中(使用锁适当地确保值在上述情况下不会更改)。