0

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

MyClass{
private:
   Substructure substructure;
   QReadWriteLock rwlElem;
}

我把储物柜放在这样的存取器中:

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

我希望子结构的副本将被安全返回。和我在二传手做的一样。使用这样的储物柜是一种好习惯吗?首先会发生什么:复制基础结构或破坏储物柜?

4

1 回答 1

0

我将首先回答您的最后一个问题:

首先会发生什么:复制底层结构或破坏储物柜?

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

这已在 stackoverflow 上被多次询问,请参见此处此处此处

CWG 1885对 C++14 标准应用了更精确的措辞:

N4606 §6.6.3 [stmt.return]/3

调用结果的复制初始化在由 return 语句的操作数建立的完整表达式末尾处的临时变量的销毁之前排序,而后者又在局部变量的销毁之前排序(6.6)包含 return 语句的块。


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

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

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

和我在二传手做的一样。使用这样的储物柜是一种好习惯吗?

假设您QWriteLocker在设置器中使用过,这应该没问题(从某种意义上说,它不会导致未定义的行为)。但是你必须注意你的界面真正在说什么。例如,当您mc对 type的实例执行类似操作时MyClass

Substructure s = mc.getSub();
if(s == anotherSubstructure)
    mc.doSomething();

当执行上述代码的线程与 进行比较sanotherSubstructure,另一个线程可能会更改实际的substructureinside mc,而前一个线程看不到这种更改(因为它有自己的 副本substructure)。

关键是,当你这样做时Substructure s = mc.getSub();,你唯一知道的是它的子结构在某个时间点mc等于,并且一旦你释放函数中的锁,这个值可能会改变。s getSub

在我上面的例子中,doSomething()不能假设mc' 子结构的值仍然等于anotherSubstructure。您可能需要将需要在其自己的函数中具有原子性的操作分组(通过适当的锁确保在上述情况下值不会改变)。

于 2017-08-08T13:10:04.660 回答