C++11并发与多线程总结(二) --独占互斥锁--mutex,lock_guardy与其他mutex

文章目录

(一) 线程概念、创建及传参
(二) 独占互斥锁–mutex,lock_guardy与其他mutex
(三) unique_lock替换lock_guardy
(四) 单例模式(Singleton)下的线程安全问题
(五) window临界区
(六) condition_variable条件变量
(七) std::async异步任务与std::future< >
(八) packaged_task< >与promise< >
(九) 原子操作atomic<>简介

(1)锁的引入与死锁
  1. 由于创建多个线程时,各个线程之间是顺序不定的执行,由系统调度。
  2. 对于共享数据,多个线程同时对其内存操作会造成异常
  3. 操作共享数据时候,为了保护共享数据,需要锁住共享数据。
  4. 注意两个以上互斥锁可能会产生死锁问题,即两个锁相互等待对方释放,程序锁死不动

(2)mutex与lock_guard

1.list在按顺序存取效率高,vector无序效率高
2.多个线程同时读写共享数据必定会出错,系统不稳定
3.std::mutex对象 lock() unlock() 必须成对,否则死锁
4.std::lock_guard()类模板 自动解锁,不能再使用lock() unlock() 离开作用域析构解
5.死锁,至少有两个互斥量,互斥量之间相互等待
6.一般解决方案 lock顺序一致
7.使用std::lock(1,2)同时锁或不同时锁
9.std::lock()与std::lock_guard()混合使用 传入std::adopt_lock() 即类内部不调用lock

  1. mutex使用方法
	std::mutex m_mutex;
	m_mutex.lock()
	//...
	//操作共享数据
	//...
	m_mutex.unlock()
  1. lock_guard使用
    当项目过大时,有可能互斥量过多会忘记解锁,导致程序问题,因此为了方便引入lock_guard自动解锁
	std::mutex m_mutex;
	std::lock_guard<mutex> lockguard(m_mutex); //传入互斥量
	//...
	//操作共享数据,离开作用域自动解锁
	//...
  1. lock_guard第二参数
	std::mutex m_mutex;
	m_mutex.lock();  //混合使用
	std::lock_guard<mutex> lockguard(m_mutex, std::adopt_lock); //内部不再上锁
	//...
	//操作共享数据,离开作用域自动解锁
	//...
  • 详见代码段1


(3)recursive_mutex与timed_mutex
  • 由于多个线程之间操作共享资源需要上锁,但可能线程调用了需要上锁的函数或线程,所以可能会重复上锁,使用lock_guard会导致重复上锁而崩溃,因此引入递归锁

(1)recursive_mutex递归独占互斥量
1.可以多次lock
2.效率更低,小号更大
3.递归次数有限次,超出则发生异常
4.不应滥用,而是考虑代码空间优化


(2)std::timed_mutex带超时的独占互斥量
1.时间段 std::chrono::milliseconds time(1000); //定义1000毫秒
2.try_lock_for(time)返回布尔值 传入等待时间,若超过等待时长未拿到锁,则解除堵塞继续执行
3.try_lock_until(chrono::steady_clock::now() + time)返回布尔值,传入未来时间点,同上


(3)recursive_timed_mutex用法类推



- 代码段1
#include 
#include
#include
#include

using namespace std;


class CReadAndWrite
{
public:
	CReadAndWrite() {};
	void read();
	void wirte();

private:
	list<int> m_list;
	mutex m_mutex1;
	mutex m_mutex2;
};

void CReadAndWrite::wirte()
{
	for (int i = 0; i < 1000; i++)
	{
		//std::lock(m_mutex1, m_mutex2);  //死锁解决方法,但以下需传入 adopt_lock

		//std::lock_guard l1(m_mutex1,std::adopt_lock);  
		//std::lock_guard l2(m_mutex2, std::adopt_lock);

		std::lock_guard<mutex> l1(m_mutex1);  //和以下顺序不一样产生死锁
		std::lock_guard<mutex> l2(m_mutex2);
		m_list.push_back(i);
		cout << "wirite  " << i << endl;
	}
}

void CReadAndWrite::read()
{
	for (int i = 0; i < 10000; i++)
	{
		if (m_list.size())
		{
			//std::lock(m_mutex1, m_mutex2);

			//std::lock_guard l2(m_mutex2, std::adopt_lock);
			//std::lock_guard l1(m_mutex1, std::adopt_lock);

			std::lock_guard<mutex> l1(m_mutex1);//和以上顺序不一样产生死锁
			std::lock_guard<mutex> l2(m_mutex2);
			int num = m_list.front();
			cout << "输出 num = " << num << endl;
			m_list.pop_front();
		}
	}
}



int main()
{


	CReadAndWrite obj;
	thread mywrite(&CReadAndWrite::wirte, &obj);
	thread myread(&CReadAndWrite::read, &obj);

	mywrite.join();
	myread.join();

	std::cout << "Main end!\n";
}


你可能感兴趣的:(C++11并发与多线程总结)