将std::lock_guard包含在额外范围内
Including std::lock_guard in extra scope
将std::lock_guard
放在一个额外的作用域中以使锁定期尽可能短,这样做有意义吗?
伪代码:
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
{ // open new scope
std::lock_guard<std::mutex> lock(mut);
shared_var = newValue;
} // close the scope
... // do some other stuff (that might take longer)
除了锁定持续时间短之外,还有其他优点吗?
什么可能是负面副作用?
是的,将锁保护的范围限制为尽可能短是有意义的,但不能更短
持有锁的时间越长,线程就越有可能阻塞等待该锁,这会影响性能,因此通常认为这是一件坏事。
但是,您必须确保程序仍然正确,并且在必须的时候,即访问或修改由锁保护的共享资源时,始终保持锁。
可能还有一点需要考虑(我在这里没有足够的实践经验来肯定地发言)。锁定/释放互斥体可能是一种性能成本极高的操作。因此,可能会发现,保持锁的时间稍长,而不是解锁&在一次操作过程中多次重新锁定它实际上可以提高整体性能。这是剖析可以向你展示的东西。
这可能有一个缺点:不能用这种方式保护初始化。例如:
{
std::lock_guard<std::mutex> lock(mut);
Some_resource var{shared_var};
} // oops! var is lost
你必须使用这样的分配:
Some_resource var;
{
std::lock_guard<std::mutex> lock(mut);
var = shared_Var;
}
这对于某些类型来说可能是次优的,对于这些类型,默认初始化后进行分配的效率不如直接初始化。此外,在某些情况下,初始化后无法更改变量。(例如const
变量)
user32434999指出了这个解决方案:
// use an immediately-invoked temporary lambda
Some_resource var {
[&] {
std::lock_guard<std::mutex> lock(mut);
return shared_var;
} () // parentheses for invoke
};
这样,您可以保护检索过程,但初始化本身仍然没有受到保护。
是的,这是有道理的。
没有其他优点,也没有副作用(这是一个很好的写作方式)。
一个更好的方法是将其提取到一个私有成员函数中(如果您有一个以这种方式同步的操作,您还可以为该操作命名):
{
// all used variables beside the lock_guard are created and initialized somewhere else
...// do something
set_var(new_value);
... // do some other stuff (that might take longer)
}
void your_class::set_value(int new_value)
{
std::lock_guard<std::mutex> lock(mut);
shared_var = new_value;
}
使用额外的作用域来限制std::lock_guard对象的生存期确实是一种很好的做法。正如其他答案所指出的,将互斥锁锁定最短的时间将减少另一个线程阻塞互斥锁的机会。
我看到了另一个答案中没有提到的问题:事务操作。让我们用两个银行账户之间转账的经典例子。为了使您的银行程序正确,必须在不解锁之间的互斥锁的情况下修改两个银行帐户的余额。否则,当程序处于一种奇怪的状态时,另一个线程可能会锁定互斥锁,其中只有一个帐户被记入贷方/借方,而另一个帐户的余额没有受到影响!
考虑到这一点,当每个共享资源被修改时,仅仅确保互斥锁是不够的。有时,您必须将互斥锁锁定一段时间,该时间跨越所有形成事务的共享资源的修改。
编辑:
如果由于某种原因,在事务的整个持续时间内保持互斥锁是不可接受的,则可以使用以下算法:
1。锁定互斥,读取输入数据,解锁互斥
2。执行所有需要的计算,将结果保存在本地
3。锁定互斥锁,检查输入数据是否没有更改,执行具有现成结果的事务,解锁互斥锁。
如果在执行步骤2的过程中输入数据发生了变化,则丢弃结果,重新使用新的输入数据。
我看不出有什么理由这么做。如果你做一些简单的事情,比如"设置一个变量"-使用atomic<>而且你根本不需要互斥和锁。如果您做了一些复杂的事情——将这些代码提取到新函数中,并在第一行使用lock。
- 为什么在全局范围内使用"extern int a"似乎不行?
- 错误:未在此范围内声明'reverse'
- 并行用于C++17中数组索引范围内的循环
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 不计算一个范围内的完美数
- 错误:"imread"未在此范围内声明
- 我在范围内未声明的错误类有问题
- 如何在cpp中使用地图显示给定日期范围内(在下面的问题中)的费率?
- 我有一个数组,我想输入一个范围,然后找到范围内所有偶数的总和?
- 未在此范围内声明错误 'xy'
- 在C++中使用变量而不是"#define"来指定数组大小是不是一种糟糕的做法?(C错误:在文件范围内
- 包含文件中的 Typdef "未在此范围内声明"
- 尽管包含正确的文件,但在此范围内未声明
- 为什么即使在启用 C++11 并且我包含字符串之后,'stod'仍然没有在此范围内声明?
- 将std::lock_guard包含在额外范围内
- 如何在包含不同数字的范围内找到整数
- -O1/2/3 与 -std=c++1y/11/98 - 如果包含,<cmath>我收到错误:"_hypot"未在此范围内声明
- 包含的功能在以后的范围内不可用
- C++:为什么在包含标头时收到"未在此范围内声明"错误?
- 错误:方法未在此范围内声明(但已包含)