使用锁保护器和互斥锁的首选方式是什么
What is the prefered way of using lock guard and mutex
class 成员函数将在其 critical section
或 critical data
上使用 mutex
和 lock_guard
。我可以看到这可以通过两种不同的方式完成。
案例 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++ 最佳实践进行一些说明。
您正在解锁互斥量以便随后立即将其锁定。会发生什么取决于互斥锁的实现方式,但典型的 non-fair 实现会唤醒一个等待线程,但会在该线程能够 运行 之前获取互斥锁,从而浪费执行时间。
如果您的互斥实现是公平的(想想 ticket lock),那么您的线程将无法在解锁后锁定互斥,并且必须等到另一个线程离开临界区。这意味着在争用下您的线程将不得不在每次迭代时进行上下文切换,从而浪费执行时间。
所以第二种情况(循环外的互斥量)在公平和 non-fair 互斥量实现上应该更有效,这就是你应该做的。
现在,如果您关心延迟,您可能会考虑在每次迭代时锁定互斥体,因为这允许其他线程 运行,但这仅在公平的互斥体实现中才有意义。
C++ 没有说明 std::mutex
是否公平,大多数实现都是不公平的。所以不要期望太高。
所以唯一明智且可移植的方法是将锁放在循环之外。因为即使你关心延迟,std::mutex
也帮不了你。
class 成员函数将在其 critical section
或 critical data
上使用 mutex
和 lock_guard
。我可以看到这可以通过两种不同的方式完成。
案例 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++ 最佳实践进行一些说明。
您正在解锁互斥量以便随后立即将其锁定。会发生什么取决于互斥锁的实现方式,但典型的 non-fair 实现会唤醒一个等待线程,但会在该线程能够 运行 之前获取互斥锁,从而浪费执行时间。
如果您的互斥实现是公平的(想想 ticket lock),那么您的线程将无法在解锁后锁定互斥,并且必须等到另一个线程离开临界区。这意味着在争用下您的线程将不得不在每次迭代时进行上下文切换,从而浪费执行时间。
所以第二种情况(循环外的互斥量)在公平和 non-fair 互斥量实现上应该更有效,这就是你应该做的。
现在,如果您关心延迟,您可能会考虑在每次迭代时锁定互斥体,因为这允许其他线程 运行,但这仅在公平的互斥体实现中才有意义。
C++ 没有说明 std::mutex
是否公平,大多数实现都是不公平的。所以不要期望太高。
所以唯一明智且可移植的方法是将锁放在循环之外。因为即使你关心延迟,std::mutex
也帮不了你。