我对线程之间共享的不同变量做了一些冗长的分配。将这些分配拆分到单个作用域中以防止单个lock_guard占用所有变量是否有意义?
如果前一个线程移至第二个作用域,则下一个线程可以使用第一个变量。
我的猜测是,这取决于调用函数的复杂性。如果您只有一个任务,那肯定比第二个lock_guard锁定/解锁另一个互斥锁要快。
伪代码
// all used variables beside the lock_guards are created and initialized somewhere else
// do something ...
{
std::lock_guard<std::mutex> lock(mut0);
shared_var0 = SomeFunctionTakingSameTime0();
}
{
std::lock_guard<std::mutex> lock(mut1);
shared_var1 = SomeFunctionTakingSameTime1();
}
// do this again or other stuff ...
这种类型的代码结构可能有什么优点或缺点?
答案 0 :(得分:1)
根据您的描述,我认为SomeFunctionTakingSameTime0()
有点耗时。但是,与共享变量交互的唯一方法是赋值。因此,最好执行以下操作:
auto temp = SomeFunctionTakingSameTime0();
{
std::lock_guard<std::mutex> lock(mut0);
shared_var0 = std::move(temp);
}
由于您为锁提供了两个不同的互斥体mut0
和mut1
,所以最好的办法是分别获取它们。
答案 1 :(得分:1)
将这些分配拆分到单个作用域中以防止单个lock_guard占用所有变量是否有意义?
是的。确实,您的直觉是正确的。
这种类型的代码结构可能有什么优点或缺点?
但是,用一小段代码就无法预测这种情况。最好的建议是执行一些基准测试。
我个人会考虑一些重要方面:
shared_var0
的类型?SomeFunctionTakingSameTime1
的时间?如您所说,如果您的函数SomeFunctionTakingSameTime0
和SomeFunctionTakingSameTime1
将花费相当多的时间,那么分成两个不同的作用域可以帮助最大化吞吐量。
通常,关键部分(即您持有锁的范围)应尽可能短。
当然,像其他所有东西一样,需要权衡取舍。 lock
/ unlock
操作不是免费的。
int shared;
lock(mutex);
shared = 1;
unlock(mutex);
lock(mutex);
shared = 2;
unlock(mutex);
当然,这是一个愚蠢的例子。但这显示了锁定获取操作中的关键方面。
该示例中的赋值操作比两次锁定互斥锁便宜得多!
此外,这将阻止某些编译器优化(从语义的角度来看,可以完全删除赋值shared = 1
)。
最后,如果shared_var0
和shared_var1
的类型是基本类型(int
,float
等),您甚至可以考虑将它们存储到{ {1}},并完全避免互斥。