在逗号运算符的LHS中初始化匿名互斥锁定类实例

时间:2009-09-08 11:23:37

标签: c++ mutex raii

假设我的代码是这样的:

#include "boost/thread/mutex.hpp"

using boost::mutex;
typedef mutex::scoped_lock lock;

mutex mut1, mut2;

void Func() {
 // ...
}

void test_raiicomma_1() {
 lock mut1_lock(mut1);
 Func();
}

void test_raiicomma_2() {
 (lock(mut1)), Func();
}

void test_raiicomma_3() {
 (lock(mut1)), (lock(mut2)), Func(); // Warning!
}

int main()
{
 test_raiicomma_1();
 test_raiicomma_2();
 test_raiicomma_3();
 return 0;
}

如果函数test_raiicomma_1()是从多个线程调用的,它会锁定一个互斥锁,以防止任何其他线程同时调用Func()。构造变量mut1_lock时互斥锁被锁定,当它超出范围并被破坏时被释放。

这完全没问题,但作为一种风格问题,需要为持有锁的临时对象命名,这让我感到烦恼。函数test_raiicomma_2()试图通过对锁对象进行初始化并在一个表达式中调用函数Func()来避免这种情况。

Func()返回后,在表达式结束之前不会调用临时对象析构函数是否正确? (如果是这样,你认为使用这个成语是否值得,或者在单独的声明中声明锁是否总是更清楚?)

如果函数test_raiicomma_3()需要锁定两个互斥锁,那么在调用Func()之前互斥锁将被按顺序锁定是否正确,之后是否释放,但遗憾的是可能会以任何顺序释放?< / p>

3 个答案:

答案 0 :(得分:4)

  

在Func()返回后,在表达式结束之前不会调用临时对象析构函数是否正确?

可以保证调用构造函数和析构函数,因为它们有副作用,并且只有在完整表达式结束时才会发生破坏。

我相信它应该有用

  

如果函数test_raiicomma_3()需要锁定两个互斥锁,那么在调用Func()之前互斥锁将按顺序锁定是否正确,之后是否释放,但可能会以任何顺序释放?

总是从左到右评估逗号,并且范围内的自动变量总是以创建的相反顺序销毁,所以我认为它甚至保证它们也以(正确)顺序发布

正如litb在评论中指出的那样,你需要大括号或你的表达式将被解析为声明。

  

(如果是这样,你认为使用这个成语是否值得,或者在单独的声明中声明锁定总是更清楚?)

我不这么认为,对于非常非常小的收获感到困惑...... 我总是使用非常明确的锁和非常明确的范围(通常在一个块中额外的{}),不错的线程安全代码很难,没有“特殊”代码,并且在我看来需要非常清晰的代码。

YMMW当然:)

答案 1 :(得分:3)

虽然不必提供有意义的名称可以解除您的负担,但它会添加任务以找出代码应该对代码读者的负担做些什么 - 在任务之上决定是否{{1实际上它的作者似乎应该如此。

鉴于一段代码被写入一次,但是被读取了10次,100次或1000次,是否真的很难写出所有的锁?

答案 2 :(得分:0)

互斥锁将按从左到右的顺序创建,并在完整表达式结束时释放。你可以写:

 // parentheses tells that it is full expression and set order explicitly
 ( ( lock(mut1), lock(mut2) ), Func() ); 

根据C ++标准5.18 / 1,互斥锁将按正确顺序销毁:

  

逗号运算符从左到右分组   表达式:
  赋值表达式
  表达式,赋值表达式

     

用逗号分隔的一对表达式从左到右进行计算,左表达式的值为   丢弃。左值到右值(4.1),数组到指针(4.2)和函数到指针(4.3)标准收敛 -   sions不适用于左表达式。左表达的所有副作用(1.9),除了   在评估正确的表达之前进行临时的破坏(12.2)。类型和   结果的值是右操作数的类型和值;如果右操作数为。

,则结果为左值