C++11并发与多线程笔记(5)互斥量概念、用法、死锁演示及解决详解

C++11并发与多线程笔记(5)互斥量概念、用法、死锁演示及解决详解

  • 1、互斥量(mutex)的基本概念
  • 2、互斥量的用法
    • 2.1 lock(),unlock()
    • 2.2 lock_guard类模板
  • 3、死锁
    • 3.1 死锁演示
    • 3.2 死锁的一般解决方案:
    • 3.3 std::lock()函数模板
    • 3.4 std::lock_guard的std::adopt_lock参数

1、互斥量(mutex)的基本概念

  • 互斥量就是个类对象,可以理解为一把,多个线程尝试用**lock()**成员函数来加锁,只有一个线程能锁定成功,如果没有锁成功,那么流程将卡在lock()这里不断尝试去锁定。
  • 互斥量使用要小心,保护数据不多也不少,少了达不到效果,多了影响效率。

2、互斥量的用法

包含#include 头文件

2.1 lock(),unlock()

步骤如下:
1. 引入头文件
2. 创建一个互斥量
3.在加锁位置:先lock() ,操作共享数据 ,unlock()

注意:lock()和unlock()要成对使用
例1:

#include
#include
#include 
#include
#include
using namespace std;

class A {
public:
	//把收到的消息(玩家命令)加入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 100000; i++) {
			cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl;//cout只输出i,不访问共享数据

			my_mutex.lock();
			msgRecvQueue.push_back(i);// 假设这个数字i就是收到的命令,直接弄到消息队列里边来
			my_mutex.unlock();
		}
	}

	bool outMsgLULProc(int& command) {
		my_mutex.lock();
		if (!msgRecvQueue.empty()) {
			//消息不为空
			command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在
			msgRecvQueue.pop_front();//移除第一个元素,但不返回
			my_mutex.unlock();//有分支退出,就要加一个unlock与其对应
			return true;
		}
		my_mutex.unlock();
		return false;
	}

	//把数据从消息队列中取出的线程
	void outMsgRecvQueue() {
		int command = 0;
		for (int i = 0; i < 100000; i++) {
			bool result = outMsgLULProc(command);
			if (result == true) {
				cout << " outMsgRecvQueue()执行,取出一个元素" << command << endl;
				//可以进行命令(数据)处理
			}else {
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
			cout << "end" << endl;
		}
	}
	
private:
	list<int> msgRecvQueue;//容器,专门用于存放玩家发来的命令
	mutex my_mutex;//创建一个互斥量
};

int main() {
	A myobja;
	//线程通过对象的成员函数时,第二个参数需要是对象的地址,因为成员函数运行时需要用到this指针
	thread mythreadInMsgRecv(&A::inMsgRecvQueue, &myobja);
	thread mythreadOutMsgRecv(&A::outMsgRecvQueue, &myobja);
	mythreadInMsgRecv.join();
	mythreadOutMsgRecv.join();
	
	cout << "I love China!" << endl;
	return 0;
}

例2:

#include 
#include 
#include 
#include 
using namespace std;

list<int> test_list;
mutex my_mutex;//创建互斥量

void in_list(){
    for(int num=0;num<10000;num++){
        my_mutex.lock();
        cout<<"插入数据: "<<num<<endl;
        test_list.push_back(num);
        my_mutex.unlock();
    }

}

void out_list(){
    for(int num=0;num<10000; ++num){
        my_mutex.lock();
        if(!test_list.empty()){
            int tmp = test_list.front();
            test_list.pop_front();
            cout<<"取出数据:"<<tmp<<endl;

        }
        my_mutex.unlock();

    }
}
int main()
{


    thread in_thread(in_list);
    thread out_thread(out_list);
    in_thread.join();
    out_thread.join();
    cout << "Hello World!" << endl;

    return 0;
}


2.2 lock_guard类模板

  • lock_guard 对象名(myMutex);取代lock()和unlock()
  • lock_guard 构造函数执行了mutex::lock();在作用域结束时,调用析构函数,执行mutex::unlock()
  • 可以通过添加{}来减小作用域
#include 
#include 
#include 
#include 
using namespace std;

list<int> test_list;
mutex myMutex;

void in_list() {
    for (int num = 0; num < 10000; num++) {
        { //也可以多加一对括号,减小作用域
            lock_guard<mutex> myGuard(myMutex); //创建lock_guard对象
            cout << "插入数据: " << num << endl;
            test_list.push_back(num);
        }
    }
}

void out_list() {
    for (int num = 0; num < 10000; ++num) {
        lock_guard<mutex> myGuard(myMutex);
        if (!test_list.empty()) {
            int tmp = test_list.front();
            test_list.pop_front();
            cout << "取出数据:" << tmp << endl;
        }
    }
}
int main()
{
    thread in_thread(in_list);
    thread out_thread(out_list);
    in_thread.join();
    out_thread.join();
    cout << "I love China!" << endl;
    return 0;
}

3、死锁

3.1 死锁演示

死锁至少有两个互斥量mutex1mutex2

  1. 线程A执行时,这个线程先锁 mutex1,并且锁成功了,然后去锁mutex2的时候,出现了上下文切换
  2. 上下文切换后,线程B执行,这个线程先锁mutex2,因为mutex2没有被锁,即mutex2可以被锁成功,然后线程B要去锁mutex1.
  3. 此时,死锁产生了,A锁着mutex1,需要锁mutex2,B锁着mutex2,需要锁mutex1,两个线程没办法继续运行下去。

3.2 死锁的一般解决方案:

只要保证多个互斥量上锁的顺序一样就不会造成死锁。

3.3 std::lock()函数模板

  • std::lock(mutex1,mutex2……); 一次锁定多个互斥量(一般这种情况很少),用于处理多个互斥量。
  • 如果互斥量中一个没锁住,它就等着,等所有互斥量都锁住,才能继续执行。如果有一个没锁住,就会把已经锁住的释放掉(要么互斥量都锁住,要么都没锁住,防止死锁)
  • 破坏请求与保持条件,资源一次性申请,一次性释放
#include 
#include 
#include 
#include 
using namespace std;

list<int> test_list;
mutex myMutex1;
mutex myMutex2;

void in_list() {
    for (int num = 0; num < 10000; num++) {
        lock(myMutex1, myMutex2);//一次锁定多个互斥量
        cout << "插入数据: " << num << endl;
        test_list.push_back(num);
        myMutex2.unlock();
        myMutex1.unlock();
    }
}

void out_list() {
    for (int num = 0; num < 10000; ++num) {
        lock(myMutex1, myMutex2);
        if (!test_list.empty()) {
            int tmp = test_list.front();
            test_list.pop_front();
            cout << "取出数据:" << tmp << endl;
        }
        myMutex1.unlock();
        myMutex2.unlock();
    }
}
int main()
{
    thread in_thread(in_list);
    thread out_thread(out_list);
    in_thread.join();
    out_thread.join();
    cout << "I love China!" << endl;
    return 0;
}

3.4 std::lock_guard的std::adopt_lock参数

采用std::lock函数和std::lock_guard 来联合使用:

  • my_guard(my_mutex,std::adopt_lock);
    加入adopt_lock后,在调用lock_guard的构造函数时,不再进行lock();
  • adopt_guard为结构体对象,起一个标记作用,表示这个互斥量已经lock(),不需要在lock()
#include 
#include 
#include 
#include 
using namespace std;

list<int> test_list;
mutex myMutex1;
mutex myMutex2;

void in_list() {
    for (int num = 0; num < 10000; num++) {
        lock(myMutex1, myMutex2); //lock联合lock_guard使用

        lock_guard<mutex> mutex1(myMutex1, adopt_lock);
        lock_guard<mutex> mutex2(myMutex2, adopt_lock);
        cout << "插入数据: " << num << endl;
        test_list.push_back(num);
    }
}

void out_list() {
    for (int num = 0; num < 10000; ++num) {
        lock(myMutex1, myMutex2);
        lock_guard<mutex> mutex1(myMutex1, adopt_lock);
        lock_guard<mutex> mutex2(myMutex2, adopt_lock);
        if (!test_list.empty()) {
            int tmp = test_list.front();
            test_list.pop_front();
            cout << "取出数据:" << tmp << endl;
        }
    }
}
int main()
{
    thread in_thread(in_list);
    thread out_thread(out_list);
    in_thread.join();
    out_thread.join();
    cout << "I love China!" << endl;
    return 0;
}

你可能感兴趣的:(C++11并发与多线程笔记,c++,笔记)