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

发布于 2024-08-03 17:34:46 字数 957 浏览 8 评论 0原文

假设我有这样的代码:

#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() 之前按顺序锁定,并在之后释放,但不幸的是可能会以任一顺序释放?

Suppose I have code something like this:

#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;
}

If the function test_raiicomma_1() were called from multiple threads, it locks a mutex to prevent any other thread also calling Func() at the same time. The mutex is locked when the variable mut1_lock is constructed, and released when it goes out of scope and is destructed.

This works perfectly fine, but as a matter of style, needing to give a name to the temporary object holding the lock irked me. The function test_raiicomma_2() attempts to avoid this, by inialising the lock object and calling the function Func() within one expression.

Is it correct that the temporary object destructor will not be called until the end of the expression, after Func() has returned? (If so, do you think it's ever worthwhile to use this idiom, or is it always clearer to declare the lock in a separate statement?)

If the function test_raiicomma_3() needs to lock two mutexes, is it correct that the mutexes will be locked in order before calling Func(), and released afterwards, but may unfortunately be released in either order?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

蹲在坟头点根烟 2024-08-10 17:34:46

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

确保构造函数和析构函数都被调用,因为它们有副作用,并且销毁只会在完整表达式的末尾发生。

我相信它应该有效

如果函数 test_raiicomma_3() 需要锁定两个互斥体,则在调用 Func() 之前按顺序锁定互斥体,然后释放互斥体,但不幸的是可能会以任一顺序释放互斥体,这是否正确?

逗号总是从左到右评估,作用域内的自动变量总是以创建的相反顺序被销毁,所以我认为甚至可以保证它们也以(正确的)顺序释放

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

(如果是这样,您认为值得使用这个习惯用法,还是在单独的语句中声明锁总是更清晰?)

我不这么认为,不,为了非常非常小的收益而感到困惑......
我总是使用非常显式的锁和非常显式的作用域(通常在块内额外的 {}),如果没有“特殊”代码,体面的线程安全代码就已经足够困难了,并且在我看来保证了非常清晰的代码。

当然是YMMW:)

Is it correct that the temporary object destructor will not be called until the end of the expression, after Func() has returned?

It is guaranteed that both constructor and destructor are called, as they have side effects, and that destruction will only happen at the end of a full expression.

I believe it should work

If the function test_raiicomma_3() needs to lock two mutexes, is it correct that the mutexes will be locked in order before calling Func(), and released afterwards, but may unfortunately be released in either order?

Comma is always evaluated left to right, and automatic variables within a scope are always destroyed in reverse order of creation, so I think it's even guaranteed they are released in the (correct) order too

As litb points out in the comments, you need braces or your expression will be parsed as a declaration.

(If so, do you think it's ever worthwhile to use this idiom, or is it always clearer to declare the lock in a separate statement?)

I don't think so no, to confusing for very, very little gain...
I always use very explicit locks, and very explicit scopes (often extra {} within a block), decent threadsafe code is hard enough without 'special' code, and warrants very clear code in my opinion.

YMMW of course :)

夏尔 2024-08-10 17:34:46

虽然不必提供有意义的名称可以减轻您的负担,但它会增加找出该代码应该做什么的任务,从而增加代码读者的负担 - 在决定是否 test_raiicomma_3 实际上按照其作者所设想的方式工作。

一段代码只写一次,但被读10次、100次、1000次,那么写所有的锁真的那么难吗?

While not having to give meaningful names frees you of a burden, it will add the task to find out what that code is supposed to do to the burdens of readers of your code -- atop the task to decide whether test_raiicomma_3 actually works the way its writer seems to have supposed.

Given that a piece of code is written once, but is read 10, 100, or 1000 times, is it really so hard to write all the locks?

白云悠悠 2024-08-10 17:34:46

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

 // 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)是在评估正确表达式之前执行的。类型和
结果的值是右操作数的类型和值;如果其右操作数是,则结果是左值。

Mutexes will be created in left-to-right order and released in the end of full expression. You could write:

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

Mutexes will be destroyed in proper order according to C++ Standard 5.18/1:

The comma operator groups left-to-right.
expression:
assignment-expression
expression , assignment-expression

A pair of expressions separated by a comma is evaluated left-to-right and the value of the left expression is
discarded. The lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3) standard conver-
sions are not applied to the left expression. All side effects (1.9) of the left expression, except for the
destruction of temporaries (12.2), are performed before the evaluation of the right expression. The type and
value of the result are the type and value of the right operand; the result is an lvalue if its right operand is.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文