类成员函数将在其mutex
或lock_guard
上使用critical section
和critical data
。我可以看到这可以通过两种不同的方式完成。
案例1: - for循环内部。 lock_guard
在每次迭代时构造和销毁。
std::mutex s_mutex;
class Foo {
public:
void bar() {
for ( ... ) {
std::lock_guard<std::mutex> guard( s_mutex );
// critical section data
} // lock_guard goes out of scope and releases or unlocks mutex
}
};
案例2: - for循环之外。 lock_guard
创建一次,然后在循环完成后销毁。
std::mutex s_mutex;
class Foo {
public:
void bar() {
std::lock_guard<std::mutex> guard( s_mutex );
for ( ... ) {
// data
}
} // lock_guard goes out of scope releasing or unlocking mutex.
};
我知道在第一种情况下,一个线程可以在一次迭代中访问循环,而另一个线程可以在不同的迭代上访问循环,但没有两个线程可以同时访问临界区。至于第二种情况,我知道如果一个线程正在访问循环,第二个线程在完全完成之前就无法触及该循环。
一种方法比另一种更理想,还是取决于使用意图?是否会对另一个产生性能影响?只是想要一些澄清来试图维护现代c ++最佳实践。
答案 0 :(得分:3)
您正在解锁互斥锁以立即锁定它。发生什么取决于mutex的实现方式,但典型的非公平实现会唤醒一个等待的线程,但会在该线程能够运行之前获取互斥锁,浪费执行时间。
如果您的互斥锁实现是公平的(考虑ticket lock),那么您的线程在解锁后无法锁定互斥锁,并且必须等到另一个线程离开临界区。这意味着在竞争中你的线程必须在每次迭代时进行上下文切换,浪费执行时间。
所以第二种情况(循环外部的互斥体)在公平和非公平的互斥体实现方面应该更有效,这就是你应该做的。
现在你可以考虑在每次迭代时锁定互斥锁,如果你关心延迟,因为这允许其他线程运行,但这只有合理的互斥实现才有意义。
C ++没有说明std::mutex
是否合理,大多数实现都不公平。所以不要期待太多。
所以唯一合理且便携的方法是将锁放在循环之外。因为即使您关心延迟,std::mutex
也不能帮助您。