mutex.lock(),lock_gard<std::mutex>,unique_lock的区别

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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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更消耗系统资源。

posted @ 2020-08-02 21:45  cyssmile  阅读(913)  评论(0编辑  收藏  举报