c++11线程基本用法
简单示例
thread和join()
#include<iostream> #include<thread> using namespace std; void test() { cout << "我的线程执行" << endl; for (int i = 0; i < 10; ++i); cout << "我的线程执行完毕" << endl; } int main() { //thread是标准库的类,test为可调用对象,作为线程执行起点 thread th(test); //join():阻塞主线程,让主线程等待子线程执行完毕 th.join(); cout << "主线程执行结束" << endl; return 0; }
detach()
//传统多线程程序主线程要等待子线程执行完毕后才能退出 //detach():将主线程和子线程分离。可以让主线程不必等待子线程 //一旦detach()之后,与主线程关联的thread对象就会视区和主线程的关联,此时这个子线程就会在后台运行。当子线程执行完成后,由语和女性时库负责清理该线程相关资源 //detach()会让子线程失去我们的控制,使用detach()后不能join() #include<iostream> #include<thread> using namespace std; void test() { cout << "我的线程执行" << endl; for (int i = 0; i < 10; ++i); cout << "我的线程执行完毕" << endl; } int main() { thread th(test); th.detach(); cout << "主线程执行完毕" << endl; return 0; }
主线程执行完毕后,子线程由运行时库接管,无法打印出来
joinable()
//joinable():判断是否可以成功join()或者detach(),返回true或者false #include<iostream> #include<thread> using namespace std; void test() { cout << "我的线程执行" << endl; for (int i = 0; i < 10; ++i); cout << "我的线程执行完毕" << endl; } int main() { thread th(test); if (th.joinable()) { cout << "1:joinable()==true" << endl; } else { cout << "1:joinable()==false" << endl; } th.detach(); if (th.joinable()) { cout << "2:joinable()==true" << endl; } else { cout << "2:joinable()==false" << endl; } cout << "主线程执行完毕" << endl; return 0; }
其他创建线程的方法
类对象作为可调用对象
#include<iostream> #include<thread> using namespace std; class Test { public: void operator()() {//不能带参数 cout << "我的线程开始执行" << endl; for (int i = 0; i < 10; ++i); cout << "我的线程执行完毕" << endl; } }; int main() { Test test; thread th(test); th.join(); cout << "主线程执行完毕" << endl; return 0; }
若把join()改成detach(),主线程执行结束,test还在吗?
如果对象不在了,线程还能继续执行吗?
这个对象实际上是被复制到线程中去,执行完线程后,test会被销毁,但是复制的对象还在
#include<iostream> #include<thread> using namespace std; class Test { public: Test(){ cout << "Test()构造函数被执行" << endl; } Test(const Test& test) { cout << "拷贝构造函数执行" << endl; } ~Test() { cout << "析构函数执行" << endl; } void operator()() {//不能带参数 cout << "线程开始执行" << endl; for (int i = 0; i < 10; ++i); cout << "线程执行完毕" << endl; } }; int main() { Test test; thread th(test); th.detach(); for (int i = 0; i < 10; ++i) { cout << "主线程执行完毕" << i << endl; } return 0; }
将detach()改为join()后
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201006200716787.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQzMzI2NzQ0,size_16,color_FFFFFF,t_70#pic_center)
用lambda表达式
#include<iostream> #include<thread> using namespace std; int main() { auto test = [] { cout << "线程开始执行" << endl; for (int i = 0; i < 10; ++i); cout << "线程执行完毕" << endl; }; thread th(test); th.join(); cout << "主线程执行完毕" << endl; return 0; }
传递临时对象作为线程参数
可调用对象带有参数时
#include<iostream> #include<thread> using namespace std; //传入的不是myNum的引用,指向地址不同,实际是值传递,即便主线程detach(),子线程使用num不会出问题 //传入的指针依旧是指向muBuf的地址,所以detach后,子线程会出问题。可以将char*改为const string& //使用const string&也有问题,即:不知道mybuf不知道什么时候转换为string,如果主线程执行完毕后,还没转换完毕,就有问题 void test(const int& num, char* buf) { cout << "线程开始执行" << endl; cout << num << endl; cout << buf << endl; cout << "线程执行完毕" << endl; } int main() { int myNum = 1; int& num = myNum; char myBuf[] = "only for test!"; thread th(test, num, myBuf); th.join(); cout << "主线程执行完毕" << endl; return 0; }
// 生成一个临时string对象,绑定const string& //使用这个方法,就会让string对象在主线程执行完毕前构造 //在创建线程的同时构造临时对象是可行的 thread th(test,num,string(myBuf));
总结(针对detach())
(1)若传递int这种简单类型参数,建议都是值传递,不要用引用
(2)如果传递类对象,避免隐式类型转换。全部都要在创建线程就构建临时对象,然后函数参数中用引用,否则系统还会构造一次
- 线程id的概念
(1)每个线程(包括主线程)实际都对应着一个数字,并且数字都不同
(2)线程id可以用c++标准库中的函数来获取,即:std::this_thread::get_id()
临时对象构造时机抓捕
#include<iostream> #include<thread> using namespace std; class A { public: int num; //类型转换构造函数,可以把一个int转换成一个类A对象 A(int num) :num(num) { cout << "构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } A(const A& a) :num(a.num) { cout << "拷贝构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } ~A() { cout << "析构函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } }; void test(const A& a) { cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl; } int main() { cout << "主线程id是" << std::this_thread::get_id() << endl; int num = 2; thread th(test, num); th.join(); cout << "主线程执行完毕" << endl; return 0; }
A类对象在子线程中构造的,若改为detach()后,当主线程执行完毕后,还没构造,就出现问题了
```c++
//在创建线程就构建临时对象
thread th(test, A(num));
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201006200754483.png#pic_center)
传递类对象、智能指针作为线程参数
线程参数为应用时修改值,不会影响主线程的值
#include<iostream> #include<thread> using namespace std; class A { public: mutable int num;//标注为const也能修改 //类型转换构造函数,可以把一个int转换成一个类A对象 A(int num) :num(num) { cout << "构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } A(const A& a) :num(a.num) { cout << "拷贝构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } ~A() { cout << "析构函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } }; void test(const A& a) { a.num = 199;//修改不会影响main的值 cout << "a.num=" << a.num << endl; cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl; } int main() { cout << "主线程id是" << std::this_thread::get_id() << endl; A a(10); thread th(test, a);//将类对象作为线程参数 th.join(); cout << "主线程的a.num=" << a.num << endl; cout << "主线程执行完毕" << endl; return 0; }
std::ref函数,可以让线程传入的参数不被复制(注:没使用detach())
//将线程传入参数加上std::ref thread th(test, std::ref(a));
传递智能指针
#include<iostream> #include<thread> using namespace std; void test(unique_ptr<int> uptr) { cout << "子线程id是" << std::this_thread::get_id() << endl; cout << "当前智能指针的地址是" << uptr << endl; } int main() { cout << "主线程id是" << std::this_thread::get_id() << endl; unique_ptr<int> uptr(new int(10)); cout << "当前智能指针的地址为" << uptr << endl; thread th(test, std::move(uptr));//将类对象作为线程参数 th.join(); cout << "主线程执行完毕" << endl; return 0; }
若改为detach(),当主线程执行完毕后,uptr被释放,但子线程还在执行,此时就会出现问题
用成员函数指针做线程函数
#include<iostream> #include<thread> using namespace std; class A { public: mutable int num; //类型转换构造函数,可以把一个int转换成一个类A对象 A(int num) :num(num) { cout << "构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } A(const A& a) :num(a.num) { cout << "拷贝构造函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } ~A() { cout << "析构函数执行" << this << "threadid=" << std::this_thread::get_id() << endl; } void thread_work(int num) { cout << "子线程thread_work执行了" << ",threadid=" << std::this_thread::get_id() << endl; } }; void test(const A& a) { a.num = 199;//修改不会影响main的值 cout << "a.num=" << a.num << endl; cout << "线程test的参数地址是" << &a << "threadid=" << std::this_thread::get_id() << endl; } int main() { cout << "主线程id是" << std::this_thread::get_id() << endl; A a(10); //第一个参数是对象函数指针,第二个参数对象,其后参数为函数所需参数 thread th(&A::thread_work, a, 1); th.join(); cout << "主线程的a.num=" << a.num << endl; cout << "主线程执行完毕" << endl; return 0; }
c++11线程的互斥量
创建和等待多个线程
#include<iostream> #include<thread> #include<vector> using namespace std; //线程的入口函数 void test(int num) { cout << "线程开始执行,线程编号=" << num << endl; for (int i = 0; i < 10; ++i); cout << "线程执行结束了,线程编号=" << num << endl; } int main() { cout << "主线程开始执行" << endl; //创建和等待多个线程 vector<thread> threads; //创建10个线程,线程入口统一使用test for (int i = 0; i < 10; ++i) { threads.push_back(thread(test, i));//创建10个线程,同时这10个线程开始执行 } for (auto i = threads.begin(); i != threads.end(); ++i) { i->join();//等待10个线程返回 } cout << "主线程执行结束" << endl; return 0; }
表明线程的执行是无序的,和操作系统内部对线程的调度有关
数据共享问题分析——mutex
只读的数据
#include<iostream> #include<thread> #include<vector> using namespace std; int global = 10;//共享数据 //线程的入口函数 void test(int num) { cout << "编号为" << std::this_thread::get_id() << "打印global_v的值" << global << endl; for (int i = 0; i < 10; ++i); cout << "线程执行结束了,线程编号=" << std::this_thread::get_id() << endl; } int main() { cout << "主线程开始执行" << endl; //创建和等待多个线程 vector<thread> threads; //创建10个线程,线程入口统一使用test for (int i = 0; i < 10; ++i) { threads.push_back(thread(test, i));//创建10个线程,同时这10个线程开始执行 } for (auto i = threads.begin(); i != threads.end(); ++i) { i->join();//等待10个线程返回 } cout << "主线程执行结束" << endl; return 0; }
所有线程读取的数据都相等,即:**只读数据安全稳定**
有读有写
#include<iostream> #include<thread> using namespace std; int num[10] = { 0 }; void write() { for (int i = 0; i < 10; ++i) { num[i] = 1; this_thread::sleep_for(std::chrono::milliseconds(1)); } } void read() { for (int i = 1; i < 10; ++i) { if (num[i] != num[i - 1]) { cout << "数据不一致" << endl; } } } int main() { cout << "主线程开始执行" << endl; //创建2个线程,一个线程负责读,一个负责写 thread write(write); thread read(read); write.join(); read.join(); cout << "主线程执行结束" << endl; return 0; }
共享数据的保护
- 互斥量的基本概念
是类的对象,可以理解为一把锁,多个线程尝试用lock()成员函数来加锁这把锁头,只有一个线程可以锁定成功(成功的标志是lock()函数返回),如果没有锁定成功,线程就会阻塞在这里,不断尝试加锁
互斥量的用法
- 先lock(),操作共享数据,unlock()
- lock()和unlock()要成对使用,lock()必然要有unlock,每调用一次lock(),必然要调用一次unlock()
- 注意:mutex是不可复制对象,所以mutex作为类的成员要注意。C++中thread调用“带mutex的类”的成员函数报错C2661:std::tuple解决方法_tomwillow的博客-CSDN博客
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int num[10] = { 0 }; void write() { int copy[10]; for (int i = 0; i < 10; ++i) { copy[i] = 1; this_thread::sleep_for(std::chrono::milliseconds(1)); } g_mutex.lock(); memcpy(num, copy, 10); g_mutex.unlock(); } void read() { int copy[10] = { 0 }; g_mutex.lock(); memcpy(copy, num, 10); g_mutex.unlock(); for (int i = 1; i < 10; ++i) { if (copy[i] != copy[i - 1]) { cout << "数据不一致" << endl; } } } int main() { cout << "主线程开始执行" << endl; //创建2个线程,一个线程负责读,一个负责写 thread write(write); thread read(read); write.join(); read.join(); cout << "主线程执行结束" << endl; return 0; }
- 互斥量的基本概念
![在这里插入图片描述](https://img-blog.csdnimg.cn/2020101311253696.png#pic_center)
4. 为了防止忘记unlock(),引入一个叫**std::lock_guard**的类模板。类似于智能指针
(1)std::lock_guard直接取代了lock()和unlock()
(2)lock_guard构造函数中执行了lock(),析构函数中执行了unlock()
```c++
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex g_mutex;
int num[10] = { 0 };
void write() {
int copy[10];
for (int i = 0; i < 10; ++i) {
copy[i] = 1;
this_thread::sleep_for(std::chrono::milliseconds(1));
}
std::lock_guard<mutex> write_mutex(g_mutex);
memcpy(num, copy, 10);
}
void read() {
int copy[10] = { 0 };
std::lock_guard<mutex> read_mutex(g_mutex);
memcpy(copy, num, 10);
for (int i = 1; i < 10; ++i) {
if (copy[i] != copy[i - 1]) {
cout << "数据不一致" << endl;
}
}
}
int main() {
cout << "主线程开始执行" << endl;
//创建2个线程,一个线程负责读,一个负责写
thread write(write);
thread read(read);
write.join();
read.join();
cout << "主线程执行结束" << endl;
return 0;
}
```
如果不想线程执行完毕后std::lock_guard才执行析构函数,可以将其和需要加锁的共享数据放到{}中
```c++
{
std::lock_guard<mutex> write_mutex(g_mutex);
memcpy(num, copy, 10);
}
//此时std::lock_guard属于{}这个作用域,当在{}外后,std::lock_guard生命周期结束,就会析构,即执行unlock
```
死锁
- 死锁的前提是至少有两个互斥量(锁头)才能产生
- 目前有两个互斥量,即:锁1,锁2,两个线程A,B
(1)线程A执行的时候,这个线程先锁锁1,把锁1锁成功了,然后它去锁锁2,此时出现上下文切换,线程A让出CPU
(2)线程B执行了,这个线程先锁锁2,因为锁2没有被锁,所以锁2锁成功,然后它去锁锁1
(3)此时死锁产生,线程A拿不到锁2,解不开锁1,线程B拿不到锁1,解不开锁2,两个线程就无限互相等待下去
- 死锁的一般解决办法:保证两个互斥量的lock()顺序一致
std::lock()函数模板
- 一次锁住两个或两个以上的互斥量(1个不行),它就不会存在多个线程因为锁的顺序导致的死锁风险。
- 如果互斥量中有一个没锁住,它就会一直等待,直到所有的互斥量都锁住
要么多个互斥量都锁住,要么都没有锁住。如果只锁一个其他没成功,它就会立即把已经lock()的都unlock()
mutex g_mutex1; mutex g_mutex2; std::lock(g_mutex1,g_mutex2); g_mutex1.unlock(); g_mutex2.unlock();
std::lock_guard的std::adopt_lock
//是一个结构体对象,其一个标记作用,表示互斥量已经lock() //std::adopt_lock让lock_guard不执行lock() mutex g_mutex1; mutex g_mutex2; std::lock(g_mutex1,g_mutex2); std::lock_guard<mutex> guard1(g_mutex1,std::adopt_lock); std::lock_guard<mutex> guard2(g_mutex2,std::adopt_lock);
unique_lock
(1)unique_lock是个类模板,和lock_guard类似,都是对mutex进行lock()和unlock()操作的
(2)unique_lock比lock_guard更灵活,但效率上差一点,内存占用多一点。
(3)用法和lock_guard类似
mutex g_mutex; std::unique_lock<std::mutex> uniqueLock(g_mutex);
(4)unique_lock第二个参数
a. std::adopt_lock(lock_guard也可使用):表示互斥量已经lock(),即:不需要在unique_lock的构造函数进行lock()
b. std::try_to_lock:会尝试mutex的lock()去锁定mutex,但如果没有锁定成功,就会立即返回,并不会阻塞(前提是使用try_to_lock前不能单独使用lock())
std::unique_lock<mutex> uniqueLock(g_mutex,std::try_to_lock); if(uniqueLock.owns_lock()){//尝试Lock()成功 } //实例 #include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int num[10] = { 0 }; void write() { int copy[10]; for (int i = 0; i < 10; ++i) { copy[i] = 1; } unique_lock<mutex> uniqueLock(g_mutex); this_thread::sleep_for(chrono::milliseconds(2000)); memcpy(num, copy, 10); } void read() { int copy[10] = { 0 }; unique_lock<mutex> uniqueLock(g_mutex,std::try_to_lock); if (uniqueLock.owns_lock()) { memcpy(copy, num, 10); } else { cout << "线程2没有拿到锁" << endl; } for (int i = 1; i < 10; ++i) { if (copy[i] != copy[i - 1]) { cout << "数据不一致" << endl; } } } int main() { cout << "主线程开始执行" << endl; //创建2个线程,一个线程负责读,一个负责写 thread write(write); thread read(read); write.join(); read.join(); cout << "主线程执行结束" << endl; return 0; }
c. **std::defer_lock**:并没有给mutex,即:**初始化一个没有lock()的mutex**(defer_lock的前提是不能先lock()),经常配合unique_lock的成员函数使用
6. unique_lock的重要成员函数
(1)lock()
(2)unlock():unique_lock也可以自动解锁
(3)try_lock():(类似于try_to_lock)尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,返回true,这个函数是**不阻塞**的
```c++
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex g_mutex;
int num[10] = { 0 };
void write() {
int copy[10];
for (int i = 0; i < 10; ++i) {
copy[i] = 1;
}
unique_lock<mutex> uniqueLock(g_mutex);
this_thread::sleep_for(chrono::milliseconds(2));
memcpy(num, copy, 10);
}
void read() {
int copy[10] = { 0 };
unique_lock<mutex> uniqueLock(g_mutex,defer_lock);
if (uniqueLock.try_lock() == true) {
memcpy(copy, num, 10);
}
else {
cout << "线程2没有拿到锁" << endl;
}
for (int i = 1; i < 10; ++i) {
if (copy[i] != copy[i - 1]) {
cout << "数据不一致" << endl;
}
}
}
int main() {
cout << "主线程开始执行" << endl;
//创建2个线程,一个线程负责读,一个负责写
thread write(write);
thread read(read);
write.join();
read.join();
cout << "主线程执行结束" << endl;
return 0;
}
```
![在这里插入图片描述](https://img-blog.csdnimg.cn/20201013112643439.png#pic_center)
(4)release():返回它所管理的mutex对象指针,并**释放所有权**,也就是说,这个unique_lock和mutex不再联系(unlock不会释放所有权)。
```c++
#include<iostream>
#include<thread>
#include<mutex>
using namespace std;
mutex g_mutex;
int num[10] = { 0 };
void write() {
int copy[10];
for (int i = 0; i < 10; ++i) {
copy[i] = 1;
}
unique_lock<mutex> uniqueLock(g_mutex);
mutex* ptx = uniqueLock.release();//释放mutex所有权,所以需要自己unlock()
memcpy(num, copy, 10);
ptx->unlock();
}
void read() {
int copy[10] = { 0 };
unique_lock<mutex> uniqueLock(g_mutex);
memcpy(copy, num, 10);
for (int i = 1; i < 10; ++i) {
if (copy[i] != copy[i - 1]) {
cout << "数据不一致" << endl;
}
}
}
int main() {
cout << "主线程开始执行" << endl;
//创建2个线程,一个线程负责读,一个负责写
thread write(write);
thread read(read);
write.join();
read.join();
cout << "主线程执行结束" << endl;
return 0;
}
```
7. unique_lock所有权
```c++
mutex g_mutex;
unique_lock<mutex> uniqueLock(g_mutex);//uniqueLock拥有g_mutex的所有权
//不能复制所有权,但是所有权可以转移
//转移方法1
unique_lock<mutex> uniqueLock_(move(uniqueLock));//当前uniqueLock失去g_mutex控制权,指向空,uniqueLock_指向g_mutex
//转移方法2
//从函数返回一个局部的unique_lock对象是允许的
//返回这种局部对象会导致系统生成临时的unique_lock对象,并调用unique_lock的移动构造函数
unique_lock<mutex> rtn_unique_lock(){
unique_lock<mutex> uniqueLock(g_mutex);
return uniqueLock;
}
```
单例设计模式共享数据分析、解决,call_once
单例设计模式
(1)单例:整个项目中,有某个或者某些特殊的类,并且只能创建一个属于该类的对象
(2)单例类示例
#include<iostream> using namespace std; class MyCAS {//这是一个单例类 private: MyCAS(){}//私有化构造函数 private: static MyCAS* m_instance;//静态成员变量 public: static MyCAS* getInstance() { if (m_instance == nullptr) { m_instance = new MyCAS(); static Release release;//当程序退出时执行析构函数 } return m_instance; } class Release {//用来释放对象 public: ~Release() { if (MyCAS::m_instance!=nullptr) { delete m_instance; MyCAS::m_instance = nullptr; } } }; }; //类静态变量初始化 MyCAS* MyCAS::m_instance = nullptr; int main() { MyCAS* ptr = MyCAS::getInstance();//创建一个对象,返回该类(对象)指针 MyCAS* ptr_ = MyCAS::getInstance(); cout << "ptr=" << ptr << endl; cout << "ptr_=" << ptr_ << endl; return 0; }
单例设计模式共享数据问题分析、解决
问题:在创建的线程(非主线程)中创建单例类对象,并且这种线程可能不止一个,就需要将getInstance()这种成员函数互斥
原因:当第一个线程在new之前失去cpu,第二个线程就会执行new一次,此时第一个线程再次执行,就又会new一次,这就不符合单例设计模式了
示例:
#include<iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; class MyCAS {//这是一个单例类 private: MyCAS(){}//私有化构造函数 private: static MyCAS* m_instance;//静态成员变量 public: static MyCAS* getInstance() { //提高效率,只有第一次没初始化时会加锁 if (m_instance == nullptr) {//双重锁定(双重检查) unique_lock<mutex> lock(g_mutex);//自动加锁 if (m_instance == nullptr) { m_instance = new MyCAS(); static Release release;//当程序退出时执行析构函数 } } return m_instance; } class Release {//用来释放对象 public: ~Release() { if (MyCAS::m_instance!=nullptr) { delete m_instance; MyCAS::m_instance = nullptr; } } }; void fun() { cout << "测试" << endl; } }; //类静态变量初始化 MyCAS* MyCAS::m_instance = nullptr; void thread1() { cout << "线程th1开始执行" << endl; MyCAS* ptr = MyCAS::getInstance(); cout << "线程th1执行完毕" << endl; } void thread2() { cout << "线程th2开始执行" << endl; MyCAS* ptr = MyCAS::getInstance(); cout << "线程th2执行完毕" << endl; } int main() { thread th1(thread1); thread th2(thread2); th1.join(); th2.join(); return 0; }
std::call_once()——c++11标准
函数的第二个参数是一个函数名,需要和std::once_flag标记结合使用
功能:保证函数只被调用一次,即:通过std::once_flag这个标记来判断函数是否执行,当调用call_once()成功后,call_once()就把这个标记设置为一种已调用状态。后续再次调用call_once(),只要once_falg被设置为已调用,就不会执行了
具备互斥量的能力
#include<iostream> #include<thread> #include <mutex> using namespace std; once_flag g_flag;//这是系统定义的标记 class MyCAS {//这是一个单例类 static void CreateInstance() {//只被调用一次 m_instance = new MyCAS(); static Release release;//当程序退出时执行析构函数 } private: MyCAS(){}//私有化构造函数 private: static MyCAS* m_instance;//静态成员变量 public: static MyCAS* getInstance() { //两个线程同时执行到这里,其中一个线程要等另外一个线程执行完毕CreateInstance后,就会放弃执行CreateInstance call_once(g_flag, CreateInstance); return m_instance; } class Release {//用来释放对象 public: ~Release() { if (MyCAS::m_instance!=nullptr) { delete m_instance; MyCAS::m_instance = nullptr; } } }; void fun() { cout << "测试" << endl; } }; //类静态变量初始化 MyCAS* MyCAS::m_instance = nullptr; void thread1() { cout << "线程th1开始执行" << endl; MyCAS* ptr = MyCAS::getInstance(); cout << "线程th1执行完毕" << endl; } void thread2() { cout << "线程th2开始执行" << endl; MyCAS* ptr = MyCAS::getInstance(); cout << "线程th2执行完毕" << endl; } int main() { thread th1(thread1); thread th2(thread2); th1.join(); th2.join(); return 0; }
# c++11线程的条件变量
轮询机制:每隔一定时间,进行查询
缺点:查询不能太频繁(浪费CPU),也不能太不频繁(缓冲区满),难以把握。性能不佳
#include <iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; int g_buf[100];//缓冲区,最多存放一百个数 int g_count = 0; //第一个线程:生产者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一个1……20之间的随机数 this_thread::sleep_for(chrono::milliseconds(50 * r));//休息时间在50-1000毫秒之间 //存放一个物品(这里存放的数据代表物品) g_mutex.lock(); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_mutex.unlock(); } } //第二个线程:消费者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50)); g_mutex.lock(); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } g_mutex.unlock(); } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }
条件变量——std::condition_variable
(1)std::condition_variable实际是一个类,是一个和条件相关的一个类,即:等待一个条件达成。
(2)这个类是需要和互斥量来配合工作,用的时候我们要生成这个类的对象
(3)wait()
a. 如果第二个参数(可调用对象)返回的值是false,那么wait()将解锁互斥量,并堵塞到 本行。堵塞到其他某个线程调用notify_one()成员函数为止。如果第二个参数返回值是true,那么wait()会直接返回
b. 如果wait()没有第二个参数,那么就和第二个参数返回false效果一样。
(4)notify_one()
(1)尝试把一个wait()的线程唤醒,如果没有wait()线程,那么notify_one()就没效果,但是不会阻塞在notify_one()这里
(2)当其他线程用notify_one()将wait()【原本阻塞】,wait()就开始恢复干活了,即
a. 不断尝试重新获取互斥量锁,如果获取不到,线程就阻塞在wait()这里等着获取锁。 b. 如果获取到就继续执行;获取到锁就执行lock()。
如果wait有第二个参数,就判断这个参数返回值,如果返回值为false,wait()又会对互斥量解锁,并再次阻塞,等待notify_one()唤醒
如果wait没有第二个参数,则线程继续执行
(5)示例
#include <iostream> #include<thread> #include<mutex> using namespace std; mutex g_mutex; condition_variable g_cond;//生成一个条件变量对象 int g_buf[100];//缓冲区,最多存放一百个数 int g_count = 0; //第一个线程:生产者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一个1……20之间的随机数 this_thread::sleep_for(chrono::milliseconds(50 * r));//避免本线程notify_one()后比wait()先拿到锁,休息时间在50-1000毫秒之间 //存放一个物品(这里存放的数据代表物品) unique_lock<mutex> lock(g_mutex); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_cond.notify_one(); } } //第二个线程:消费者 void Consume() { while (true) { unique_lock<mutex> lock(g_mutex); g_cond.wait(lock, [&] {//lambda为可调用对象 if (g_count > 0) return true; return false; }); for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }
- 上述代码深入思考
(1) 当删除this_thread::sleep_for(chrono::milliseconds(50 * r))后,放入和消耗就可能不会交替执行,因为notify_one()后,线程可能先于wait()拿到锁
(2)注意:notify_one()不一定起作用,但不会阻塞。因为执行notify_one()时,如果没有其他线程wait(),它就没有效果
- notify_all()
当程序中有多个线程使用wait()时,使用notify_one()只会在某时刻唤醒其中任意一个线程,另外其他线程依然在阻塞中,即:任意时刻只有一个wait()尝试拿锁,其他都在阻塞中
使用notify_all()会将所有wait()的线程都唤醒,所有线程的wait()都会尝试拿锁
c++11 async、future、packaged_task、promise
std::asyc、std::future创建后台任务并返回值
- std::asyc:是一个函数模板,用来启动一个异步任务,启动一个异步任务后,它会返回一个std::future(类模板)对象
- 启动一个异步任务:就是自动创建一个线程并开始执行对应的线程入口函数,它返回一个std::future对象
- std::future对象里就含有线程入口函数返回的结果(线程返回的结果),可以通过调用future对象的成员函数get()来获取结果
- std::future:提供了一种访问异步操作结果的机制,即:这个结果可能无法马上到达,但是不久的将来,当线程执行完毕的时候,就可以拿到结果
示例
#include<iostream> #include<future> using namespace std; int myThread() {//线程入口函数 cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; future<int> result = async(myThread);//创建一个线程并执行,但是主函数不会阻塞在这里,会继续向下执行 cout << "continue……!" << endl; cout << result.get() << endl;//主函数会阻塞在这里,等待线程返回 cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
#include<iostream> #include<future> using namespace std; class Thread { public: int myThread(int num) {//线程入口函数 cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } }; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; Thread th; future<int> result = async(&Thread::myThread,&th,12);//创建一个线程并执行,但是主函数不会阻塞在这里,会继续向下执行 cout << "continue……!" << endl; cout << result.get() << endl;//主函数会阻塞在这里,等待线程返回 cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
- 上述程序通过future对象的get()成员函数等待线程结束并返回结果,即:会阻塞future对象所在的线程,并返回结果
- future对象的get()成员函数只能调用一次
- future对象还有一个wait()成员函数,该函数只是等待线程结束,本身不会返回结果
额外向std::async()传递一个参数,该参数类型是std::launch类型(枚举类型),来达到一些特殊的目的
(1)std::launch::deferred:不会创建新线程,通过future对象调用get()或者wait()函数,就会直接调用async中的可调用对象
#include<iostream> #include<future> using namespace std; class Thread { public: int myThread(int num) {//线程入口函数 cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } }; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; Thread th; future<int> result = async(launch::deferred, &Thread::myThread, &th, 12); cout << "continue……!" << endl; cout << result.get() << endl; cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
(2)**std::launch::async**:强制这个异步任务在新线程上执行,即:系统必须创建出新线程执行可调用对象
(3)**std::launch::async | std::launch::deferred**:这是**async的默认值**,表示调用async的行为可能是创建新线程并立即执行,或者没有创建新线程并延迟到调用get()或者wait()才开始执行任务入口函数
- std::async和std::thread的区别:
(1)std::async不一定会创建新线程执行可调用对象。std::thread会创建新线程,如果系统资源紧张,创建线程失败,那么整个程序就会崩溃
(2)std::async调用方法可以用简单的get()函数拿到线程可调用对象的返回值
(3)std::thread创建的线程过多,可能创建失败,系统报告异常,崩溃。而std::async一般不会报告异常、崩溃,因为当系统资源紧张导致无法创建新线程的时候,std::async使用默认值调用时就不会创建新线程,而是后序当某个线程使用get()获取返回值时,就在该线程执行可调用对象
std::async使用默认值
当std::async使用默认值作为参数时,会产生不确定性【即:是否创建新线程】
借助future的wait_for()来判断是否创建新线程
#include<iostream> #include<thread> #include<atomic> #include<future> using namespace std; atomic<int> g_num = 0; int myThread() { cout << "myThread() start, " << "thread_id=" << this_thread::get_id() << endl; cout << "myThread() end, " << "thread_id=" << this_thread::get_id() << endl; return 1; } int main() { cout << "main() start, " << "thread_id=" << this_thread::get_id() << endl; future<int> result = async(myThread); future_status status = result.wait_for(0s);//等待chrono::seconds(0) if (status == future_status::deferred) { cout << "没有创建新线程" << endl; cout << result.get() << endl;//此时才执行myThread() } else { //创建了新线程 if (status == future_status::ready) { cout << "线程成功创建" << endl; cout << result.get() << endl; } else if (status == future_status::timeout) { //超时,线程还没执行完毕 cout << "超时,线程还在执行中" << endl; cout << result.get() << endl; } } cout << "main() end, " << "thread_id=" << this_thread::get_id() << endl; return 0; }
std::packaged_task
- std::packaged_task:是一个类模板,模板参数是各种可调用对象,通过std::packaged_task把各种可调用对象包装起来,方便将来作为线程入口函数
- packaged_task:包装的对象还是可以直接调用的
- 可以通过get_future()获取future对象,从而取得线程的返回值
示例
#include<iostream> #include <future> #include<thread> using namespace std; int myThread(int num) {//线程入口函数 cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(int)> myFunc(myThread);//将函数myThread通过packaged_task包装起来 thread th(std::ref(myFunc), 1); th.join(); std::future<int> result = myFunc.get_future(); cout << result.get() << endl; cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
#include<iostream> #include <future> #include<thread> using namespace std; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(int)> myFunc([](int num) { cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; }); thread th(ref(myFunc), 12); th.join(); cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
#include<iostream> #include <future> #include<thread> using namespace std; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(int)> myFunc([](int num) { cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; }); myFunc(12);//直接调用 future<int> result = myFunc.get_future(); cout << result.get() << endl;//注意,没有创建新线程,而是触发lambda表达式执行,即:相当于函数调用 cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
#include<iostream> #include <future> #include<thread> #include<vector> using namespace std; vector<packaged_task<int(int)>> vec; int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(int)> myFunc([](int num) { cout << num << endl; cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; }); vec.push_back(move(myFunc));//这里用到了移动语义,此时myFunc为空 packaged_task<int(int)> myFunc_ = move(vec.back()); vec.pop_back(); myFunc_(123); future<int> result = myFunc_.get_future(); cout << result.get() << endl; cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
std::promise
std::promise也是一个类模板,能够在某个线程中给它赋值,然后可以在其他线程中,将这个值取出使用
#include<iostream> #include<thread> #include <future> using namespace std; void myThread(promise<int>& temp, int num) { //模拟完成一系列复杂操作 this_thread::sleep_for(chrono::milliseconds(5000)); temp.set_value(num);//将结果保存到promise对象中 } int main() { promise<int> myTemp;//声明一个promise对象,保存类型为int thread th(myThread, ref(myTemp), 520); th.join(); //获取结果值 future<int> result = myTemp.get_future();//promise和future绑定,用于获取线程返回值 cout << result.get() << endl; return 0; }
#include<iostream> #include<thread> #include <future> using namespace std; void myThread(promise<int>& temp, int num) { //模拟完成一系列复杂操作 this_thread::sleep_for(chrono::milliseconds(5000)); temp.set_value(num);//将结果保存到promise对象中 } void myThread_(future<int>& temp) { int result = temp.get(); cout << "myThread_ result=" << result << endl; } int main() { promise<int> myTemp;//声明一个promise对象,保存类型为int thread th(myThread, ref(myTemp), 520); th.join(); future<int> result = myTemp.get_future();//promise和future绑定,用于获取线程返回值 thread th_(myThread_, ref(result)); th_.join(); return 0; }
future其他成员函数、shared_future、atomic
std::future其它成员函数
wait_for(time):阻塞time时长,若time时长后,线程还没返回,则wait_for返回future_status::timeout。如果在time时长之内,线程成功返回,则wait_for()返回future_status::ready。如果async第一个参数设置为std::launch::deferred,则wait_for()返回future_status::deferred
#include<iostream> #include<future> using namespace std; int myThread() {//线程入口函数 cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(1000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; future<int> result = async(myThread);//创建一个线程并执行,但是主函数不会阻塞在这里,会继续向下执行 cout << "continue……!" << endl; //枚举类型 future_status status = result.wait_for(chrono::seconds(2)); if (status == future_status::timeout) { cout << "timeout" << endl; } else if (status == future_status::ready) { cout << "成功返回" << endl; cout << result.get() << endl; } else if (status == future_status::deferred) { cout << "deferred" << endl; cout << result.get() << endl; } cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
std::shared_future
std::future只能调用一次get()函数,因为get()函数的设计是一个移动语义
std::shared_future:也是一个类模板,get()函数是复制数据
#include<iostream> #include <future> #include<thread> using namespace std; int myThread() {//线程入口函数 cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(5000)); cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; return 5; } void myThread_(shared_future<int>& temp) { cout << "myThread_() start," << "threadid=" << this_thread::get_id() << endl; cout << "myThread_():" << temp.get() << endl; cout << "myThread_() end," << "threadid=" << this_thread::get_id() << endl; } int main() { cout << "main() start," << "threadid=" << this_thread::get_id() << endl; packaged_task<int(void)> myFunc(myThread);//将函数myThread通过packaged_task包装起来 thread th(std::ref(myFunc)); th.join(); shared_future<int> result = myFunc.get_future(); cout << "main():"<< result.get() << endl; thread th_(myThread_, std::ref(result)); th_.join(); cout << "main() end," << "threadid=" << this_thread::get_id() << endl; return 0; }
std::atomic——原子操作
原子操作概念及其范例
(1)原子操作:是指“不可分割的操作”,要么执行成功,要么失败,即:是在多线程中不会打断的程序执行片段
(2)原子操作:比互斥量的效率更胜一筹
(3)互斥量加锁一般针对的是一个代码片段,而原子操作针对的一般都是一个变量,而不是一个代码片段
(4)std::atomic:类模板,用来代表原子操作
(5)示例
#include<iostream> #include<thread> #include<atomic> using namespace std; atomic<int> g_num = 0; void Write() { for (int i = 0; i < 1000000; ++i) { //可替换成g_num+=1; ++g_num;//操作为原子操作,在多线程中不会被打断 } } int main() { thread th1(Write); thread th2(Write); th1.join(); th2.join(); cout<<"两个线程执行完毕,最终g_num="<<g_num<<endl; return 0; }
#include<iostream> #include<thread> #include<atomic> using namespace std; atomic<bool> g_flag = false;//线程退出标记 void myThread() { cout << "myThread() start," << "threadid=" << this_thread::get_id() << endl; while (!g_flag) { cout << "myThread() run," << "threadid=" << this_thread::get_id() << endl; this_thread::sleep_for(chrono::milliseconds(1000)); } cout << "myThread() end," << "threadid=" << this_thread::get_id() << endl; } int main() { thread th1(myThread); thread th2(myThread); this_thread::sleep_for(chrono::milliseconds(2000)); g_flag = true; th1.join(); th2.join(); return 0; }
(6)atomic原子操作,针对++,--,+=,-=,*=,&=,!=是支持的,其它可能不支持
#include<iostream> #include<thread> #include<atomic> using namespace std; atomic<int> g_num = 0; void Write() { for (int i = 0; i < 1000000; ++i) { g_num = g_num + 1; } } int main() { thread th1(Write); thread th2(Write); th1.join(); th2.join(); cout << "两个线程执行完毕,最终g_num=" << g_num << endl; return 0; }
(7)load()函数:以原子方式读取atomic对象的值
atomic<int> atm1; atomic<int> atm2=atm1;//尝试引用已删除的函数,拷贝赋值运算符也不让用 atomic<int> atm3(atm1.load())
(8)store()函数:以原子方式写入内容
atomic<int> atm; atm.store(1);
windows临界区、其他各种mutex互斥量
windows临界区
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; #define WINDOWS #ifdef WINDOWS CRITICAL_SECTION winsec;//windows中的临界区,类似于mutex #endif mutex g_mutex; int g_buf[100];//缓冲区,最多存放一百个数 int g_count = 0; //第一个线程:生产者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一个1……20之间的随机数 this_thread::sleep_for(chrono::milliseconds(50 * r));//休息时间在50-1000毫秒之间 //存放一个物品(这里存放的数据代表物品) #ifdef WINDOWS EnterCriticalSection(&winsec);//进入临界区,相当于mutex.lock() g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; LeaveCriticalSection(&winsec);//退出临界区,相当于mutex.unlock() #else g_mutex.lock(); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_mutex.unlock(); #endif // WINDOWS } } //第二个线程:消费者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//轮询 #ifdef WINDOWS EnterCriticalSection(&winsec); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } LeaveCriticalSection(&winsec); #else g_mutex.lock(); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } g_mutex.unlock(); #endif // WINDOWS } } int main() { #ifdef WINDOWS InitializeCriticalSection(&winsec);//使用临界区之前初始化 #endif // WINDOWS srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }
- 多次进入临界区试验
(1)在同一个线程中,windows中相同临界区变量代表的临界区的进入(EnterCriticalSection)可以被多次调用,但是EnterCriticalSection的调用次数需要和LeaveCriticalSection的调用次数要相等(否则,该线程始终都在临界区中,该线程一直执行,另一个线程阻塞)
(2)c++11中mutex不允许lock多次
自动析构技术
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; #define WINDOWS //用于自动释放windows下的临界区,防止忘记LeaveCriticalSection //类似与std::lock_guard //RAII类(Resource Acquisition initialization):资源获取即初始化 class cWinLock { public: cWinLock(CRITICAL_SECTION* ptr) :ptr(ptr) { EnterCriticalSection(ptr); } ~cWinLock() { LeaveCriticalSection(ptr); } private: CRITICAL_SECTION* ptr; }; #ifdef WINDOWS CRITICAL_SECTION winsec;//windows中的临界区,类似于mutex #endif mutex g_mutex; int g_buf[100];//缓冲区,最多存放一百个数 int g_count = 0; //第一个线程:生产者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一个1……20之间的随机数 this_thread::sleep_for(chrono::milliseconds(50 * r));//休息时间在50-1000毫秒之间 //存放一个物品(这里存放的数据代表物品) #ifdef WINDOWS cWinLock wLock(&winsec); cWinLock wLock_(&winsec); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; #else lock_guard<mutex> lock(g_mutex); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; #endif // WINDOWS } } //第二个线程:消费者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//轮询 #ifdef WINDOWS cWinLock wLock(&winsec); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } #else lock_guard<mutex> lock(g_mutex); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } #endif // WINDOWS } } int main() { #ifdef WINDOWS InitializeCriticalSection(&winsec);//使用临界区之前初始化 #endif // WINDOWS srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }
recursive_mutex递归的独占互斥量
std::mutex:独占互斥量,在本线程lock后,本线程无法继续lock【除非unlock后】,其它线程也无法lock
std::recursive_mutex:递归的独占互斥量,允许同一个线程中同一个互斥量多次被lock【第一次lock后没有unlock】,效率上比mutex低
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; recursive_mutex g_mutex; int g_buf[100];//缓冲区,最多存放一百个数 int g_count = 0; //第一个线程:生产者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一个1……20之间的随机数 this_thread::sleep_for(chrono::milliseconds(50 * r));//休息时间在50-1000毫秒之间 //存放一个物品(这里存放的数据代表物品) lock_guard<recursive_mutex> lock(g_mutex); lock_guard<recursive_mutex> lock_(g_mutex); g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; } } //第二个线程:消费者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//轮询 lock_guard<recursive_mutex> lock(g_mutex); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }
带超时的互斥量std::timed_mutex和std::recursive_timed_mutex
std::timed_mutex:带超时功能的独占互斥量
(1)try_lock_for():参数是一段时间,等待一段时间,如果等待超时或者lock成功,继续流程
#include <iostream> #include<thread> #include<mutex> #include<Windows.h> using namespace std; timed_mutex g_mutex;//带超时功能的独占互斥量 int g_buf[100];//缓冲区,最多存放一百个数 int g_count = 0; //第一个线程:生产者 void Producer() { while (true) { int r = rand() % 20 + 1;//生成一个1……20之间的随机数 //存放一个物品(这里存放的数据代表物品) if (g_mutex.try_lock_for(10ms)) {//等待100毫秒尝试lock //在100毫秒内lock成功 g_buf[g_count] = r; ++g_count; cout << "放入物品:" << r << endl; g_mutex.unlock(); } else { //没有在100毫秒内lock成功 cout << "Producer lock失败" << endl; this_thread::sleep_for(chrono::microseconds(100)); } } } //第二个线程:消费者 void Consume() { while (true) { this_thread::sleep_for(chrono::milliseconds(50));//轮询 lock_guard<timed_mutex> lock(g_mutex); if (g_count > 0) { for (int i = 0; i < g_count; ++i) { cout << "消耗物品:" << g_buf[i] << endl; } g_count = 0; } } } int main() { srand(time(nullptr)); thread producer(Producer); thread consume(Consume); producer.join(); consume.join(); return 0; }
(2)try_lock_until:参数是一个未来时间点,在这个未来时间没到的时间内,不管lock是否成功,流程继续
```c++
#include <iostream>
#include<thread>
#include<mutex>
#include<Windows.h>
using namespace std;
timed_mutex g_mutex;//带超时功能的独占互斥量
int g_buf[100];//缓冲区,最多存放一百个数
int g_count = 0;
//第一个线程:生产者
void Producer() {
while (true) {
int r = rand() % 20 + 1;//生成一个1……20之间的随机数
//存放一个物品(这里存放的数据代表物品)
if (g_mutex.try_lock_until(chrono::steady_clock::now() + 100ms)) {//当前时间加上100ms
//在以当前时刻算起的100ms内,lock成功
g_buf[g_count] = r;
++g_count;
cout << "放入物品:" << r << endl;
g_mutex.unlock();
}
else {
cout << "Producer lock失败" << endl;
this_thread::sleep_for(chrono::microseconds(100));
}
}
}
//第二个线程:消费者
void Consume() {
while (true) {
this_thread::sleep_for(chrono::milliseconds(50));//轮询
lock_guard<timed_mutex> lock(g_mutex);
if (g_count > 0) {
for (int i = 0; i < g_count; ++i) {
cout << "消耗物品:" << g_buf[i] << endl;
}
g_count = 0;
}
}
}
int main()
{
srand(time(nullptr));
thread producer(Producer);
thread consume(Consume);
producer.join();
consume.join();
return 0;
}
```
- std::recursive_timed:带超时功能的递归独占互斥量
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。