mutex/atomic
c++ basic data struct is not thread-safe, only multithreading read and no write is safe. mutex/atomic tool can solve data race, but if you use them incorrect, it will cause deadlock.
mutex
basic mutex
basic mutex is std::mutex
object, very simple rule: call member function lock
to lock it, call unlock
to unlock it. but you can not lock it when it locked, also not unlock it when it not locked, or it will cause deadlock.
std::mutex mtx;
mtx.lock();
// ...
mtx.unlock();
recursive mutex
recursive mutex is std::recursive_mutex
object, difference from basic mutex, it will not cause deadlock when you locked it twice or more, but you need to unlock it the same times as you locked it, it is slower than basic mutex.
std::recursive_mutex rmtx;
rmtx.lock();
// ...
rmtx.unlock();
guard lock
guard lock is std::guard_lock<T>
template, use feature RAII to lock/unlock mutex when object constructs and destructs. you just need a mutex object for its construct, you can not call lock/unlock for its mutex object manually.
std::mutex mtx;
std::guard_lock<std::mutex> lg(mtx);
// ...
unique lock
unique lock is std::unique_lock<T>
template, very similar with guard lock, but you can call lock/unlock for its mutex object manually, so it is always used with condition variable, it is slow than guard lock.
std::mutex mtx;
std::unique_lock<std::mutex> lg(mtx);
// ...
mtx.unlock()
// ...
mtx.lock();
atomic
once atomic
sometimes you need a code part just run once, but many threads want to do that. you can use std::once_flag
variable and std::call_once
function to avoid data race.
std::once_flag of;
void once_func()
{
// init...
}
// ...
std::call_once(of, once_func);
regular atomic
atomic is std::atomic<T>
template, T must be c++ basic type, such as int, double and so on, use this template, this variable will be atomic, most of its operation is the same to its basic type but slow a little bit.
std::atomic<int> cnt;
// ...
++cnt;
memory order
atomic has six memory order, default use std::memory_order_seq_cst
, unless very need perfermance, do not use other memory orders, its very easy to cause problem, here gives a spin lock class implements.
class spin_lock {
public:
spin_lock() {
while (flag.test_and_set(std::memory_order_acquire)) {}
}
~spin_lock() {
flag.clear(std::memory_order_release);
}
private:
std::atomic_flag flag;
};