访问器中的 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();
当执行上述代码的线程将 s
与 anotherSubstructure
进行比较时,另一个线程可能会更改 mc
中的实际 substructure
,并且此更改对前一个线程不可见(因为它有自己的 substructure
副本)。
重点是,当您执行 Substructure s = mc.getSub();
时,您唯一知道的是 mc
的子结构在某些时候等于 s
时间点 ,一旦您释放 getSub
函数中的锁定,此值可能会立即更改。
在我上面的例子中,doSomething()
不能假定 mc
的子结构的值仍然等于 anotherSubstructure
。您可能需要将需要原子化的操作分组到它们自己的函数中(使用锁适当地确保值在上述情况下不会更改)。
我有一个 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,
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();
当执行上述代码的线程将 s
与 anotherSubstructure
进行比较时,另一个线程可能会更改 mc
中的实际 substructure
,并且此更改对前一个线程不可见(因为它有自己的 substructure
副本)。
重点是,当您执行 Substructure s = mc.getSub();
时,您唯一知道的是 mc
的子结构在某些时候等于 s
时间点 ,一旦您释放 getSub
函数中的锁定,此值可能会立即更改。
在我上面的例子中,doSomething()
不能假定 mc
的子结构的值仍然等于 anotherSubstructure
。您可能需要将需要原子化的操作分组到它们自己的函数中(使用锁适当地确保值在上述情况下不会更改)。