假设我的代码是这样的:
#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>
答案 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)。类型和 结果的值是右操作数的类型和值;如果右操作数为。
,则结果为左值