类成员函数将在其mutex
或lock_guard
上使用critical section
和critical data
。我可以看到这可以通过2种不同的方式来完成。
情况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++最佳实践的方法即可。
最佳答案
您要解锁互斥锁,然后立即将其锁定。发生的情况取决于互斥锁的实现方式,但是典型的不公平实现会唤醒一个等待的线程,但是会在该线程能够运行之前抢占互斥锁,从而浪费了执行时间。
如果您的互斥量实现是公平的(请考虑ticket lock),那么您的线程将无法在解锁互斥量后将其锁定,而必须等到另一个线程离开关键部分。这意味着在争用状态下,您的线程将不得不在每次迭代时进行上下文切换,从而浪费了执行时间。
因此,第二种情况(循环外部的互斥体)在公平和不公平的互斥体实现中都应该更有效,这是您应该做的。
现在,如果您关心延迟,则可以考虑在每次迭代时都锁定互斥锁,因为这允许其他线程运行,但这仅对于公平的互斥锁实现才有意义。
C++并没有说明std::mutex
是否公平,而且大多数实现都不公平。因此,不要对此抱有太大期望。
因此,唯一明智且可移植的方法是将锁置于循环之外。因为即使您担心延迟,std::mutex
也不会对您有所帮助。
关于multithreading - 使用锁保护和互斥锁的首选方法是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49666630/