1.一般的lock()和unlock()
一般的我们会写出这样的代码
std::mutex mu;
mu.lock();
//....
mu.unlock()
这种方式我们是希望//...中间执行,保证只有有个线程进入临界区,但是这里存在一个问题,就是如果线程在临界区发生错误或者return,导致mutex没有正常的释放。
2.lock_guardstd::mutex
情况1中的问题出现,我们就选择使用lock_guardstd::mutex
std::mutex mu;
std::lock_guard guard(mu);
std::cout << msg << " " << id << std::endl;
但是这种情况,可能无法保证加锁所有的cout(也可以是其他的操作),这时我们就要改进这代码
3.LofFile
这种方式我们可以创建一个输出流,只要保证正确的加锁上我们的输出流,就可以保证多线程的安全
class LofFile
{
public:
LofFile() {
f.open("log.txt");
}
void share_print(std::string msg, int id) {
std::lock_guard guard(m_mutex);
f << msg << " " << id << std::endl;
}
~LofFile() {
f.close();
}
private:
std::mutex m_mutex;
std::ofstream f;
};
4.once_flag和call_once
情况3在我们只创建一个LofFile对象时是安全的,但是如果我们在多线程中创建了多个输出流f对象,这又是不安全的。
所以我们要在f.open()上创建一个只能创建一次的锁。
class LofFile
{
public:
void share_print(std::string msg, int id) {
std::call_once(open_flag, [&] {f.open("log.txt"); });
std::lock_guard guard(m_mutex);
f << msg << " " << id << std::endl;
}
private:
std::mutex m_mutex;
std::once_flag open_flag;
std::ofstream f;
};
5.unique_lock
std::mutex mu;
std::unique_lock mul(mu, std::defer_lock);
mul.lock();
//....
mul.unlock();
//....
mul.lock();
//....
mul.unlock();
unique_lock创建的对象比较的灵活,可以在自己想要加锁的地方lock()然后unlock(),而且可以多次使用,并且支持move(),
让使用move后,前一个对象就为空。lock_guard 对象不能move。unique_lock虽然灵活,但是也比lock_guard更消耗系统资源。