1) The mutex and lock
2) The lock_guard
4) call_once
class X {
private:
mutable std::once_flag initDataFlag;
void initData() const;
public:
data getData () const {
std::call_once(initDataFlag,&X::initData,this);
...
}
};
5) Gotchas
- Global std::lock(), locks all the mutex passed in or none. It has mechanism internally to decide which mutex to lock first to avoid potential deadlock. So it doesn't guarantee that the lock order will be as the order of mutex passed in.
std::mutex m1;
std::mutex m2;
...
{
std::lock (m1, m2); // lock both mutexes (or none if not possible)
std::lock_guard<std::mutex> lockM1(m1,std::adopt_lock);
std::lock_guard<std::mutex> lockM2(m2,std::adopt_lock);
...
} // automatically unlock all mutexes
- Global try_lock() does not provide a deadlock-avoidance mechanism, and it guarantees that the locks are tried in the order of the passed arguments.
std::mutex m1;
std::mutex m2;
int idx = std::try_lock (m1, m2); // try to lock both mutexes
if (idx < 0) { // both locks succeeded
std::lock_guard<std::mutex> lockM1(m1,std::adopt_lock);
std::lock_guard<std::mutex> lockM2(m2,std::adopt_lock);
...
} // automatically unlock all mutexes
else {
// idx has zero-based index of first failed lock
std::cerr << "could not lock mutex m" << idx+1 << std::endl;
} - std::lock() and std::try_lock() will not unlock the mutex locked after leaving the scope.