【cpp学习笔记】多线程编程
1、前言
在学习利用多线程来实现多模型的目标检测时,接触到了 condition_variable、future等多线程编程知识,感觉自己对这方面的知识还不熟悉,于是便找了一些学习资料进行学习。
2、学习内容
2.1、生产者消费者模型
如果只考虑一个生产者和一个消费者线程,生产者线程不停的在队列q中添加值,消费者线程不停的在队列中去除值,此时可以采用条件变量来唤醒消费者线程,当消费者线程判断队列为空时,释放锁,并cv.wait()。而生产者线程增加一个值后,就会使用cv.notify_one()随机的唤醒一个线程,因为这里只有一个线程,cv.notify_one() 和 cv.notify_all() 的功能一致。但是如果有多个消费者线程,则会出现问题。
#include<stdio.h>
#include<thread>
#include<queue>
#include<mutex>
#include<string>
#include<chrono>
#include<iostream>
#include<condition_variable>
#include<future>using namespace std;condition_variable cv;mutex mtx;
deque<int>q;void task1(){int i=0;while(true){unique_lock<mutex> lock(mtx);q.emplace_back(i);cv.notify_one();// cv.notify_all();//每次生产出一个数据后,就去唤醒 cv_waitif(i<999999){i++;}else{i=0;}}
}void task2(){int data = 0;while(true){unique_lock<mutex>lock(mtx);//如果队列为空,就利用wait进入休眠状态,同时释放锁,被唤醒时加锁if(q.empty()){cv.wait(lock);}if(!q.empty()){data = q.front();q.pop_front();cout<<"Get value from que:"<<data<<endl;}}
}void task2(){int data = 0;while(true){unique_lock<mutex>lock(mtx);//如果队列为空,就利用wait进入休眠状态,同时释放锁,被唤醒时加锁if(q.empty()){cv.wait(lock);}if(!q.empty()){data = q.front();q.pop_front();cout<<"Get value from que:"<<data<<endl;}}
}// void task3(){
// int data = 0;
// while(true){
// unique_lock<mutex>lock(mtx);// //如果队列为空,就利用wait进入休眠状态,同时释放锁,被唤醒时加锁
// if(q.empty()){
// cv.wait(lock);
// }// if(!q.empty()){
// data = q.front();
// q.pop_front();
// cout<<"Get value from que:"<<data<<endl;
// }
// }
// }int main(){thread t1(task1);thread t2(task2);if(t1.joinable())t1.join();if(t2.joinable())t2.join();// if(t3.joinable())// t3.join();return 0;
}
2.2、功能优化
考虑以下场景,当有一个生产者线程和两个消费者线程时,队列中只有一个元素,消费者线程2处理wiat状态,消费者1取出q中的元素正常进行打印,然后进入到下一个循环中并拿到锁,此时生产者线程增加一个元素,并随机的唤醒了消费者线程2,但是此时消费者线程拿到了锁并取出值并打印,此时消费者线程再去队列中拿值时就会引发异常,此时只需要将 其中的if修改为while,while会多次检查队列是否为空
#include<stdio.h>
#include<thread>
#include<queue>
#include<mutex>
#include<string>
#include<chrono>
#include<iostream>
#include<condition_variable>
#include<future>using namespace std;condition_variable cv;mutex mtx;
deque<int>q;//生产者线程
void prod(){int i=0;while(true){unique_lock<mutex> lock(mtx);q.emplace_back(i);//随机唤醒某一个线程cv.notify_one();// 去唤醒所有线程// cv.notify_all();//每次生产出一个数据后,就去唤醒 cv_waitif(i<999999){i++;}else{i=0;}}
}//消费者线程1
void con1(){int data = 0;while(true){unique_lock<mutex>lock(mtx);//如果队列为空,就利用wait进入休眠状态,同时释放锁,被唤醒时加锁while(q.empty()){//可能有多个线程调用了这个cv_wait,所以会导致报错cv.wait(lock);//含义如下,//lock.unlock()//cv.wait()}data = q.front();q.pop_front();cout<<"Get value from que:"<<data<<endl;}
}//消费者线程2
//如果此时增加了一个消费者线程,将会出现问题//虚假唤醒问题 即队列为空,但是消费者线程却被唤醒了,去取队列里面的值,就报错了、
//发生错误的情形如下:
//在线程1中,此时队列q中有一个值,线程1正常pop,然后打印,接着进入到下一个循环,运行到 unique_lock<mutex>lock(mtx);时
//此时队列 元素+1,然后随机的唤醒了线程2,但此时线程1拥有队列的锁,于是线程1拿到了队列中的值,然后进行的pop,此时队列为空,
//此时线程2再去拿队列中的值,就会发生异常,只需要将 if 修改为whilevoid con2(){int data = 0;while(true){unique_lock<mutex>lock(mtx);//如果队列为空,就利用wait进入休眠状态,同时释放锁,被唤醒时加锁while(q.empty()){cv.wait(lock);}data = q.front();q.pop_front();cout<<"Get value from que:"<<data<<endl;}
}int main(){thread t1(prod);thread t2(con1);thread t3(con2);if(t1.joinable())t1.join();if(t2.joinable())t2.join();if(t3.joinable())t3.join();return 0;
}
2.3 future、promise
如果需要获得线程中的值,可以采用条件变量等待子线程结束,再打印想要的值
#include<stdio.h>
#include<thread>
#include<queue>
#include<mutex>
#include<string>
#include<chrono>
#include<iostream>
#include<condition_variable>
#include<future>using namespace std;mutex mtx;
condition_variable cv;void task(int a,int b, promise<int>& ret){int ret_a = a * a;int ret_b = b * 2;ret = ret_a + ret_b;cv.notify_one();
}//这个版本导致代码太多,使用future可以优雅的解决int main(){int ret = 0;//如果需要传递引用,则需要加ref//将p传递进去thread t(task,1,2,ref(ret));unique_lock<mutex>lock(mtx);//等待线程通知,然后往下执行cv.wait(lock);cout<< "return value is" << ret<<endl;if(t.joinable())t.join();}
上述这种方式可以使用future更为优雅的解决
#include<stdio.h>
#include<thread>
#include<queue>
#include<mutex>
#include<string>
#include<chrono>
#include<iostream>
#include<condition_variable>
#include<future>using namespace std;void task(int a,int b, promise<int>& ret){int ret_a = a * a;int ret_b = b * 2;ret.set_value(ret_a+ret_b);}//这个版本导致代码太多,使用future可以优雅的解决int main(){int ret = 0;promise<int>p;future<int>f = p.get_future(); //将f与p关联起来thread t(task,1,2,ref(p));//f.get()会阻塞在这里等待 p给f传递值,且f.get()只能用一次cout <<"return ret is :" << f.get() <<endl;if(t.joinable())t.join();}
如果使用多个子线程,但其中 f.get()只能只用一次,此时可以使用 share_future
#include<stdio.h>
#include<thread>
#include<queue>
#include<mutex>
#include<string>
#include<chrono>
#include<iostream>
#include<condition_variable>
#include<future>using namespace std;void task(int a, future<int>&b, promise<int>& ret){int ret_a = a * a;//等待主线程的p_in 设置号值之后再继续int ret_b = b.get()* 2;ret.set_value(ret_a+ret_b);}//有时候 在给线程传值是,可能不需要立马传递值,可能需要过一段时间传递值int main(){int ret = 0;promise<int>p_ret;future<int>f_ret = p_ret.get_future();//延时传值promise<int>p_in;future<int>f_in = p_in.get_future();thread t(task,1,ref(f_in),ref(p_ret));//dop_in.set_value(8);//cout <<"return ret is :" << f_ret.get() <<endl;if(t.joinable())t.join();}
2.4、功能优化
上诉方法可以通过系统函数写的更为简洁
#include<stdio.h>
#include<thread>
#include<queue>
#include<mutex>
#include<string>
#include<chrono>
#include<iostream>
#include<condition_variable>
#include<future>using namespace std;int task(int a,int b){int ret_a = a * a;//等待主线程的p_in 设置号值之后再继续int ret_b = b * 2;return ret_a+ret_b;
}//为了简化上述方法,可以使用asyncint main(){//使用async可以在线程中获得返回值//async不一定创建新的线程做计算,如果使用launch::async则会开启新的线程//launch::deferred 为延迟调用,当有fu.get()时,才会开启线程future<int> fu = async(launch::async,task,1,2);cout <<"return ret is :" << fu.get() <<endl;}
3、总结
本次多多线程学习主要学习了条件变量、futrue、pormise的使用方法,希望在工作中处理多线程时,能够更优雅的解决问题。
【cpp学习笔记】多线程编程相关推荐
- python学习笔记——多线程编程
python学习笔记--多线程编程 基础不必多讲,还是直接进入python. Python代码代码的执行由python虚拟机(也叫解释器主循环)来控制.Python在设计之初就考虑到要在主循环中,同时 ...
- Java学习笔记---多线程并发
Java学习笔记---多线程并发 (一)认识线程和进程 (二)java中实现多线程的三种手段 [1]在java中实现多线程操作有三种手段: [2]为什么更推荐使用Runnable接口? [3][补充知 ...
- 0037 Java学习笔记-多线程-同步代码块、同步方法、同步锁
什么是同步 在上一篇0036 Java学习笔记-多线程-创建线程的三种方式示例代码中,实现Runnable创建多条线程,输出中的结果中会有错误,比如一张票卖了两次,有的票没卖的情况,因为线程对象被多条 ...
- ufldl学习笔记与编程作业:Multi-Layer Neural Network(多层神经网络+识别手写体编程)...
ufldl学习笔记与编程作业:Multi-Layer Neural Network(多层神经网络+识别手写体编程) ufldl出了新教程,感觉比之前的好,从基础讲起,系统清晰,又有编程实践. 在dee ...
- 学习笔记之编程达到一个高的境界就是自制脚本语言(图)
学习笔记之编程达到一个高的境界就是自制脚本语言(图) 编程达到一个高的境界就是自制脚本语言,通过这可以精通编程里面的高深的技术,如编译原理.语言处理器.编译器与解释器,这些都是代表一个程序员实力的技术 ...
- Python学习笔记:网络编程
前言 最近在学习深度学习,已经跑出了几个模型,但Pyhton的基础不够扎实,因此,开始补习Python了,大家都推荐廖雪峰的课程,因此,开始了学习,但光学有没有用,还要和大家讨论一下,因此,写下这些帖 ...
- linux学习笔记 -- 系统编程
系统编程 相关概念 概念 简易cpu结构 mmu内存管理单元 环境变量 PATH SHELL HOME LANG TERM getenv setenv unsetenv 进程控制 fork函数 get ...
- Java张孝祥视频 学习笔记 多线程
/*************************************************************************************/ 此博客主要是在观看张孝祥 ...
- Go学习笔记—多线程
多线程编程 一个进程可以包含多个线程,这些线程运行的一定是同一个程序(进程==程序),且都由当前进程中已经存在的线程通过系统调用的方式创建出来.进程是资源分配的基本单位,线程是调度运行的基本单位, ...
最新文章
- louvain算法_单细胞聚类(四)图解Leiden算法对Louvain算法的优化
- 每天一个linux命令(53)--ps命令
- 捡到vivo手机怎么清除账号_为什么现在买手机,很少会去考虑OPPO和vivo呢?看一下老板怎么说...
- 《从问题到程序:用Python学编程和计算》——导读
- android动画入门,Android动画之入门篇(一)
- windows无法打开所需的文件C:\Sources\install.wim。
- html 字体思源_CSS3嵌入字体@font-face调用字体(思源宋体regula/PingFang SC/ttf/woff)...
- 2021云上智能白皮书 附下载
- 计算机恶搞bat代码,电脑重启bat代码怎么设置 电脑整人bat代码大全
- Spring Boot使用JSP模板引擎
- java 股票数据接口_股票数据查询接口
- HTML+CSS+JS新年倒计时(实时更新)
- matlab实现简单图形的识别二
- 重磅!python获取同步输出的桌面网易云音乐歌词(内存偏移获取)
- 使用什么软件可以将PDF文件进行编辑
- 实例011 阳阳买苹果
- 招聘网探究分析报告(以描述性分析为主)
- 工作意向及规划个人建议
- 现代社会科学的优选法:谈取向运作法
- 群晖3617可以有几个网卡_星际蜗牛C款i211网卡服务器装Windows sevse 2012 R2服务器装机上...