• C++多线程基础


    对应视频:c++11并发与多线程视频课程

    线程创建、启动、结束

    示例 thread()+join()

    #include
    #include
    using namespace std;
    
    //自己创建的线程需要从一个函数(初始函数)开始运行
    void myPrint() {
    	cout << "myPrint" << endl;
    }
    
    int main() {
    
    	//程序运行起来,生成一个进程,该进程所属的主线程开始自动运行;当主线程从main()函数返回,则整个进程执行完毕
    	//主线程从main()开始执行,那么我们自己创建的线程,也需要从一个函数开始运行(初始函数),一旦这个函数运行完毕,线程也结束运行
    	//整个进程是否执行完毕的标志是:主线程是否执行完,如果主线程执行完毕了,就代表整个进程执行完毕了,此时如果其他子线程还没有执行完,也会被强行终止
    
    	//thread是C++11的线程类,myprint是可调用对象
    	thread mytobj(myPrint); //创建了线程,线程执行起点是myPrint();然后myPrint()开始执行
    	
    	//阻塞主线程(main函数),让主线程等待子线程执行完毕,然后让子线程和主线程汇合,然后主线程再往下走。
    	mytobj.join();//如果不加这行可能导致子线程还没执行完主线程就执行完了,会产生报错
    
    	cout << "main" << endl;
    
    	//一个良好的程序应该是主线程等待子线程执行完毕后,自己才能最终退出
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    detach()

    void myPrint() {
    	cout << "1" << endl;
    	cout << "2" << endl;
    	cout << "3" << endl;
    	cout << "4" << endl;
    	cout << "5" << endl;
    	cout << "6" << endl;
    }
    
    int main() {
    
    	
    	thread mytobj(myPrint); 
    	
    	//与主线程分离,主线程不用再等待子线程运行结束
    	//一旦detach()以后,主线程就和子线程失去关联,此时子线程就会驻留在后台运行
    	//这个子线程相当于被C++运行时库接管,当这个子线程执行完成后,由运行时库负责清理该线程的资源
    	//detach()会使线程失去我们的控制
    	//一旦调用了detach(),就不能再用join()
    	mytobj.detach();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    joinable()

    void myPrint() {
    	cout << "myPrint" << endl;
    }
    
    int main() {
    
    	
    	thread mytobj(myPrint);
    
    	//joinable() 判断是否可以成功使用join()或者detach()
    	cout << mytobj.joinable() << endl;
    	
    	mytobj.join();
    
    	cout << mytobj.joinable() << endl;
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    类对象创建线程

    class TA {
    public:
    	void operator()() {
    		cout << "TA" << endl;
    	}
    };
    
    int main() {
    
    	TA ta;
    	thread mytobj(ta);//ta可调用对象
    	mytobj.join();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    lambda表达式创建线程

    int main() {
    
    	auto myLambda = [] {
    		cout << "1" << endl;
    	};
    	
    	thread mytobj(myLambda);
    	mytobj.join();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    成员函数指针做线程函数

    class A {
    public:
    	int m_i;
    	A(int a) :m_i(a) {  }
    	A(const A &a) :m_i(a.m_i) {  }
    	~A() { }
    
    	//线程执行入口
    	void thread_work(int num) {
    		cout << "thread_work" << endl;
    	}
    };
    
    int main() {
    
    	A myobj(10);
    	thread mytobj(&A::thread_work, &myobj,15);//对象要用&或者std::ref(),不然会复制出一个新对象
    
    	mytobj.join();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    线程传参,detach()大坑

    传递临时对象作为线程参数

    void myprint(int i, int j) {
    	cout << i << endl;
    	cout << j << endl;
    }
    
    int main() {
    
    	thread mytojb(myprint, 1, 2);//先是可调用对象,然后依次是函数参数
    	mytojb.join();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    detach()陷阱

    陷阱1

    指针指向的空间随着主线程的运行结束可能被销毁

    void myprint(const int &i, char *pmybuf) {
    	//这里i并不是真正的引用,实际是值传递
    	//即使主线程detach了子线程,子线程中用i值仍然是安全的
    	cout << i << endl;
    
    	//指针绝对有问题,指向同样的内存空间
    	cout << pmybuf << endl;
    }
    
    int main() {
    
    	int mvar = 1;
    	char mybuf[] = "this is a test!";
    
    	thread mytojb(myprint, mvar, mybuf);//myprint的参数这里是值传递
    	mytojb.detach();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    陷阱2

    改用string以后,可能存在隐式转换在主线程运行结束以后

    void myprint(const int &i, const string &pmybuf) {
    	cout << i << endl;
    	cout << pmybuf.c_str() << endl;
    }
    
    int main() {
    
    	int mvar = 1;
    	char mybuf[] = "this is a test!";
    
    	//存在问题:mubuf到底在什么时候隐式转成string
    	//事实上存在 “mybuf都被回收了(main函数执行完了),系统才用mybuf去转string” 的情况
    	thread mytojb(myprint, mvar, mybuf);
    	
    	mytojb.detach();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    正确方法

    void myprint(const int &i, const string &pmybuf) {
    	cout << i << endl;
    	cout << pmybuf.c_str() << endl;
    }
    
    int main() {
    
    	int mvar = 1;
    	char mybuf[] = "this is a test!";
    
        //在创建线程的同时构造临时对象的方法传递参数是可行的
    	thread mytojb(myprint, mvar, string(mybuf));//直接将mybuf转成string对象,这样就可以保证在线程中用肯定有效的对象
    	
    	mytojb.detach();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    获取线程id

    线程id概念

    • id是个数字,每个线程(不管是主线程还是子线程)实际上都对应着一个数字,而且每个线程对应的这个数字都不一样
    • 线程id可以用C++标准库里的函数来获取。std::this_thread::get_id()来获取
    class A {
    public:
    	int m_i;
    	A(int a) :m_i(a) { cout << "构造函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
    	A(const A &a) :m_i(a.m_i) { cout << "拷贝构造函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
    	~A(){ cout << "析构函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
    };
    
    void myprint(const A &a) {
    	cout << "子线程a的参数地址是" << &a << " threadid=" << this_thread::get_id() << endl;
    }
    
    int main() {
    
    	cout << "主线程id" << this_thread::get_id() << endl;
    
    	int mvar = 1;
    	thread mytobj(myprint, mvar);
    
    	mytobj.join();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    运行结果

    主线程id11880
    构造函数执行0198F9BC threadid=14772
    子线程a的参数地址是0198F9BC threadid=14772
    析构函数执行0198F9BC threadid=14772
    main

    从运行结果中可以看出,隐式类型转换是在子线程中构造A类对象

    class A {
    public:
    	int m_i;
    	A(int a) :m_i(a) { cout << "构造函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
    	A(const A &a) :m_i(a.m_i) { cout << "拷贝构造函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
    	~A(){ cout << "析构函数执行" << this << " threadid=" << this_thread::get_id() << endl; }
    };
    
    void myprint(const A &a) {
    	cout << "子线程a的参数地址是" << &a << " threadid=" << this_thread::get_id() << endl;
    }
    
    int main() {
    
    	cout << "主线程id" << this_thread::get_id() << endl;
    
    	int mvar = 1;
    	thread mytobj(myprint, A(mvar));
    
    	mytobj.join();
    
    	cout << "main" << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    运行结果

    主线程id10568
    构造函数执行00AFF968 threadid=10568
    拷贝构造函数执行00D4FBC0 threadid=10568
    子线程a的参数地址是00D4FBC0 threadid=5196
    析构函数执行00AFF968 threadid=10568
    析构函数执行00D4FBC0 threadid=5196
    main

    从运行结果中可以看出,用了临时对象后,所有的A类对象都在main()函数中就已经构建完毕了

    线程中传真引用,线程中修改变量值,影响外面,std::ref()

    void myprint(int &a) {
    	cout << a << endl;
    	a = 100;
    }
    
    int main() {
    
    	int a = 1;
    	thread mytobj(myprint, ref(a));
    
    	mytobj.join();
    
    	cout << a << endl;
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    参数传智能指针(unique_ptr)

    void myprint(unique_ptr<int> p) {
    	cout << *p << endl;
    }
    
    int main() {
    
    	unique_ptr<int> p(new int(100));//独占式智能指针
    	thread mytobj(myprint, move(p));//独占式智能指针不能拷贝,只能移动
    
    	mytobj.join();
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    创建多个线程,数据共享

    创建和等待多个线程

    多个线程的执行顺序是乱的,跟操作系统内部对线程的运行调度机制有关。

    //线程入口函数
    void myprint(int inum) {
    	cout << "myprint线程开始执行," << " 线程编号=" << inum << endl;
    
    	cout << "myprint线程结束执行," << " 线程编号=" << inum << endl;
    }
    
    int main() {
    	vector<thread> mythreads;//把thread对象放入到容器里管理,方便对线程的管理
    
    	//创建10个线程,线程入口函数统一使用myprint
    	for (int i = 0; i < 10; i++) {
    		mythreads.push_back(thread(myprint, i));//创建并开始执行线程
    	}
    	for (auto iter = mythreads.begin(); iter != mythreads.end(); iter++) {
    		iter->join();//等待10个线程都返回
    	}
    
    	cout << "main" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    数据共享

    只读数据

    只读的数据,是安全稳定的,不需要特别的处理,直接读就可以

    vector<int> g_v = { 1,2,3 };//共享数据(只读)
    
    //线程入口函数
    void myprint(int inum) {
    	cout << "id为" << this_thread::get_id() << "的线程打印g_v的值" << g_v[0] << g_v[1] << g_v[2] << endl;
    }
    
    int main() {
    	vector<thread> mythreads;//把thread对象放入到容器里管理,方便对线程的管理
    
    	//创建10个线程,线程入口函数统一使用myprint
    	for (int i = 0; i < 10; i++) {
    		mythreads.push_back(thread(myprint, i));//创建并开始执行线程
    	}
    	for (auto iter = mythreads.begin(); iter != mythreads.end(); iter++) {
    		iter->join();//等待10个线程都返回
    	}
    
    	cout << "main" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    有读有写的数据

    会出问题,会报错,需要使用锁(见下一章)

    以下代码不使用锁,进程同时读写共享数据时会出问题

    class A {
    public:
    	//收到的消息放入到一个容器中
    	void inMsgRecvQueue() {
    		for (int i = 0; i < 10000; i++) {
    			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
    			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
    		}
    	}
    
    	//把消息从容器中取出
    	void outMsgRecvQueue() {
    		for (int i = 0; i < 10000; i++) {
    			if (!msgRecvQueue.empty()) {
    				//消息队列不为空
    				int msg = msgRecvQueue.front(); 
    				msgRecvQueue.pop_front();
                    
    				//处理消息......
                    
    			}
    			else {
    				//消息队列为空
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    private:
    	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
    };
    
    int main() {
    
    	A myobj;
    	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
    	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
    
    	myOutMsgObj.join();
    	myInMsgObj.join();
    
    	cout << "main" << endl;
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    互斥量

    互斥量(mutex)是个类对象,同一时刻多个线程都可以调用互斥量的成员函数lock()来尝试加锁,只有一个线程可以锁成功,成功的标志是lock()函数返回了;如果没锁成功,那么线程会卡在lock()不断地去尝试加锁。

    unlock()用于解锁。

    lock()和unlock()要成对使用。

    class A {
    public:
    	//收到的消息放入到一个容器中
    	void inMsgRecvQueue() {
    		for (int i = 0; i < 10000; i++) {
    			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
    
    			my_mutex.lock();
    			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
    			my_mutex.unlock();
    		}
    	}
    
    	bool outMsg(int &msg) {
    		my_mutex.lock();
    		if (!msgRecvQueue.empty()) {
    			//消息队列不为空
    			msg = msgRecvQueue.front();
    			msgRecvQueue.pop_front();
    			my_mutex.unlock();
    			return true;
    		}
    		my_mutex.unlock();
    		return false;
    	}
    
    	//把消息从容器中取出
    	void outMsgRecvQueue() {
    		int msg = 0;
    		for (int i = 0; i < 10000; i++) {
    			bool result = outMsg(msg);
    			
    			if (result) {
    				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;
    
    				//处理消息...
    
    			}
    			else {
    				//消息队列为空
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    private:
    	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
    	mutex my_mutex;//互斥量
    };
    
    int main() {
    
    	A myobj;
    	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
    	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
    
    	myOutMsgObj.join();
    	myInMsgObj.join();
    
    	cout << "main" << endl;
    	return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    std::lock_guard 类模板

    可以直接取代lock()和unlock(),具有创建时加锁,析构时解锁的功能(无需手动解锁)

    用了lock_guard以后,就不能使用lock()和unlock()

    lock_guard原理是构造函数里执行了lock(),析构函数里执行了unlock()

    class A {
    public:
    	//收到的消息放入到一个容器中
    	void inMsgRecvQueue() {
    		for (int i = 0; i < 10000; i++) {
    			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
    
    			my_mutex.lock();
    			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
    			my_mutex.unlock();
    		}
    	}
    
    	bool outMsg(int &msg) {
    		lock_guard<mutex> guard(my_mutex);
    		if (!msgRecvQueue.empty()) {
    			//消息队列不为空
    			msg = msgRecvQueue.front();
    			msgRecvQueue.pop_front();
    			return true;
    		}
    		return false;
    	}
    
    	//把消息从容器中取出
    	void outMsgRecvQueue() {
    		int msg = 0;
    		for (int i = 0; i < 10000; i++) {
    			bool result = outMsg(msg);
    			
    			if (result) {
    				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;
    
    				//处理消息...
    
    			}
    			else {
    				//消息队列为空
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    private:
    	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
    	mutex my_mutex;//互斥量
    };
    
    int main() {
    
    	A myobj;
    	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
    	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
    
    	myOutMsgObj.join();
    	myInMsgObj.join();
    
    	cout << "main" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    std::lock() 同时锁多个互斥量

    mutex mutex1;
    mutex mutex2;
    
    //std::lock() 函数可接受任意数量的互斥量, 该函数要么对所有参数互斥量都成功上锁,要么一个也不上锁。
    //可在一定程度上避免死锁
    lock(mutex1, mutex2);//同时锁mutex1和mutex2
    
    mutex1.unlock();
    mutex2.unlock();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    std::adopt_lock

    std::adopt_lock是个结构体对象,起一个标记作用。
    作用就表示这个互斥量已经lock(),不需要在std::lock_guard的构造函数里对mutex类对象再lock()了。

    mutex mutex1;
    mutex mutex2;
    
    lock(mutex1, mutex2);//同时锁mutex1和mutex2
    
    lock_guard<mutex> guard1(mutex1, adopt_lock);
    lock_guard<mutex> guard2(mutex2, adopt_lock);
    //mutex1.unlock();
    //mutex2.unlock();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    unique_lock

    unique_lock是个类模板,对互斥量进行加锁和解锁管理。

    unique_lock比lock_guard灵活很多,但是效率差一点,内存占用多一点。

    第二个参数缺省情况下,unique_lock和lock_guard一样

    class A {
    public:
    	//收到的消息放入到一个容器中
    	void inMsgRecvQueue() {
    		for (int i = 0; i < 10000; i++) {
    			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
    
    			unique_lock<mutex> u(my_mutex);
    			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
    		}
    	}
    
    	bool outMsg(int &msg) {
    		unique_lock<mutex> u(my_mutex);
    		if (!msgRecvQueue.empty()) {
    			//消息队列不为空
    			msg = msgRecvQueue.front();
    			msgRecvQueue.pop_front();
    			return true;
    		}
    		return false;
    	}
    
    	//把消息从容器中取出
    	void outMsgRecvQueue() {
    		int msg = 0;
    		for (int i = 0; i < 10000; i++) {
    			bool result = outMsg(msg);
    
    			if (result) {
    				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;
    
    				//处理消息...
    
    			}
    			else {
    				//消息队列为空
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    private:
    	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
    	mutex my_mutex;//互斥量
    };
    
    int main() {
    
    	A myobj;
    	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
    	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
    
    	myOutMsgObj.join();
    	myInMsgObj.join();
    
    	cout << "main" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60

    unique_lock第二个参数

    std::adopt_lock

    std::adopt_lock是一个标记,表示这个互斥量已经lock,不用在构造函数中lock。

    class A {
    public:
    	//收到的消息放入到一个容器中
    	void inMsgRecvQueue() {
    		for (int i = 0; i < 10000; i++) {
    			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
    
    			my_mutex.lock();//提前加锁
    			unique_lock<mutex> u(my_mutex,adopt_lock);
    			msgRecvQueue.push_back(i);//假设数字i就是收到的消息,放入消息队列中
    		}
    	}
    
    	bool outMsg(int &msg) {
    		my_mutex.lock();
    		unique_lock<mutex> u(my_mutex,adopt_lock);
    		if (!msgRecvQueue.empty()) {
    			//消息队列不为空
    			msg = msgRecvQueue.front();
    			msgRecvQueue.pop_front();
    			return true;
    		}
    		return false;
    	}
    
    	//把消息从容器中取出
    	void outMsgRecvQueue() {
    		int msg = 0;
    		for (int i = 0; i < 10000; i++) {
    			bool result = outMsg(msg);
    
    			if (result) {
    				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;
    
    				//处理消息...
    
    			}
    			else {
    				//消息队列为空
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    private:
    	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
    	mutex my_mutex;//互斥量
    };
    
    int main() {
    
    	A myobj;
    	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
    	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
    
    	myOutMsgObj.join();
    	myInMsgObj.join();
    
    	cout << "main" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    std::try_to_lock

    使用try_to_lock会尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,也会立即返回,并不会阻塞在那里。

    用try_to_lock的前提是不能先去lock互斥量。

    class A {
    public:
    	//收到的消息放入到一个容器中
    	void inMsgRecvQueue() {
    		for (int i = 0; i < 10000; i++) {
    
    			unique_lock<mutex> u(my_mutex,try_to_lock);
    			if (u.owns_lock()) {
    				//拿到了锁,操作共享数据
    				cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
    				msgRecvQueue.push_back(i);
    			}
    			else {
                    //没有拿到锁
    				cout << "inMsgRecvQueue()没拿到锁" << endl;
    			}
    		}
    	}
    
    	bool outMsg(int &msg) {
    
    		unique_lock<mutex> u(my_mutex);
    
    		chrono::milliseconds dura(200);
    		this_thread::sleep_for(dura);//休息200毫秒
    
    		if (!msgRecvQueue.empty()) {
    			//消息队列不为空
    			msg = msgRecvQueue.front();
    			msgRecvQueue.pop_front();
    			return true;
    		}
    		return false;
    	}
    
    	//把消息从容器中取出
    	void outMsgRecvQueue() {
    		int msg = 0;
    		for (int i = 0; i < 10000; i++) {
    			bool result = outMsg(msg);
    
    			if (result) {
    				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;
    
    				//处理消息...
    
    			}
    			else {
    				//消息队列为空
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    private:
    	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
    	mutex my_mutex;//互斥量
    };
    
    int main() {
    
    	A myobj;
    	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
    	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
    
    	myOutMsgObj.join();
    	myInMsgObj.join();
    
    	cout << "main" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72

    std::defer_lock

    defer_lock意思是构造函数的时候不需要给mutex加锁。

    class A {
    public:
    	//收到的消息放入到一个容器中
    	void inMsgRecvQueue() {
    		for (int i = 0; i < 10000; i++) {
    			cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
    
    			unique_lock<mutex> u(my_mutex, defer_lock);//没有对my_mutex加锁
    			u.lock();//加锁,不用关心解锁
    			msgRecvQueue.push_back(i);
    		}
    	}
    
    	bool outMsg(int &msg) {
    
    		unique_lock<mutex> u(my_mutex);
    
    		if (!msgRecvQueue.empty()) {
    			//消息队列不为空
    			msg = msgRecvQueue.front();
    			msgRecvQueue.pop_front();
    			return true;
    		}
    		return false;
    	}
    
    	//把消息从容器中取出
    	void outMsgRecvQueue() {
    		int msg = 0;
    		for (int i = 0; i < 10000; i++) {
    			bool result = outMsg(msg);
    
    			if (result) {
    				cout << "outMsgRecvQueue()执行,取出一个元素 " << msg << endl;
    
    				//处理消息...
    
    			}
    			else {
    				//消息队列为空
    				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
    			}
    		}
    		cout << "end" << endl;
    	}
    
    private:
    	list<int> msgRecvQueue;//专门用于存收到的消息的容器(消息队列)
    	mutex my_mutex;//互斥量
    };
    
    int main() {
    
    	A myobj;
    	thread myOutMsgObj(&A::outMsgRecvQueue, &myobj);//第二个参数加&保证在线程中用的是同一个对象
    	thread myInMsgObj(&A::inMsgRecvQueue, &myobj);
    
    	myOutMsgObj.join();
    	myInMsgObj.join();
    
    	cout << "main" << endl;
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    unique_lock的成员函数

    lock()

    给绑定的互斥量加锁。

    unlock()

    给绑定的互斥量解锁。

    可以手动unlock(),unique_lock在析构的时候会判断对应的mutex有没有unlock,如果已经unlock,就不会再次unlock

    try_lock()

    尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,则返回true,这个函数不会阻塞。

    release()

    返回它所管理的mutex对象指针,并释放所有权。

    unique_lock和mutex不再有关系。

    如果原来mutex处于加锁状态,断开关系以后需要手动解锁。

    unique_lock所有权的传递

    unique_lockmyUniLock(myMutex);把myMutex和myUniLock绑定在了一起,也就是myUniLock拥有myMutex的所有权

    使用移动语义

    • myUniLock拥有myMutex的所有权,myUniLock可以把自己对myMutex的所有权转移,但是不能复制。
    • unique_lock myUniLock2(std::move(myUniLock));
      现在myUniLock2拥有myMutex的所有权。

    在函数中return一个临时变量

    unique_lock<mutex> aFunction()
    {
        unique_lock<mutex> myUniLock(myMutex);
        //返回这种局部对象会导致系统生成临时的unique_lock对象,并调用unique_lock的移动构造函数
        return myUniLock;
    }
    // 然后就可以在外层调用,在sbguard具有对myMutex的所有权
    std::unique_lock<std::mutex> sbguard = aFunction();
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 相关阅读:
    网络安全(黑客)自学
    JS-cookie函数封装方法
    元宇宙直播:“概念期”的暴利生意,风口下的炒作游戏
    换个地方写helloworld
    使用wireshark分析tcp握手过程
    代理模式简单举例
    JVM成神之路(十一) -- JVM常用命令解析
    Java——包装类
    【Linux】安装Nginx
    解锁前端Vue3宝藏级资料 第三章 Vue Router路由器的使用
  • 原文地址:https://blog.csdn.net/qq_50710984/article/details/126327926