C++并发与多线程学习笔记--future成员函数、shared_future、atomic
- std::future的其他成员函数
- std::shared_future
- 原子操作、概念、基本用法
多线程主要是为了执行某个函数,本文的函数的例子,采用如下写法
int mythread()
{
cout << "my thread start, and thread id is " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
cout << "my thread end, and thread id is " << std::this_thread::get_id() << endl; return 5;
} int mythread1(int aa)
{
cout << "my thread start, and thread id is " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
cout << "my thread end, and thread id is " << std::this_thread::get_id() << endl; return 5;
} int mythread2(std::future<int> &tmp)
{
cout << "my thread start, and thread id is " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
cout << "my thread end, and thread id is " << std::this_thread::get_id() << endl; return 5;
}
std::future的其他成员函数
成员函数wait_for有三个返回值
如果遇到以下情况
1)主线程等待子线程执行完毕,然后返回结果,线程有三种状态,那么就要用到std::future_status
enum class future_status { // names for timed wait function returns
ready,
timeout,
deferred
};
在主函数中写
cout << "Main thead " << std::this_thread::get_id() << endl;
std::future<int> result = std::async(std::launch::deferred, mythread);
//如果第一个参数使用 std::launch::deferred,线程会被延迟执行,到了get cout << "continue....!" << endl; std::future_status status = result.wait_for(std::chrono::milliseconds(6000)); if (status == std::future_status::timeout) {
cout << "线程执行超时,线程还未执行完" << endl;
//主线程想要等待子线程的结果,如果超时,状态就会变成超时 }
else if (status == std::future_status::ready) {
cout << "线程成功执行完毕,返回" << endl;
cout << "result:" << result.get() << endl;
}
else if (status == std::future_status::deferred)
{
//如果async的第一个参数被设置为延迟执行,std::launch::deferred, 则本条件成立
cout << "线程被延迟执行" << endl;
cout << "result:" << result.get() << endl;
//这个时候实际上没有创建一个新的子线程,函数在主线程中执行
}
ready表示线程成功返回、timeout表示等待超时(线程没有成功返回)、deferrd表示延迟执行(调用get才执行)。注意async的第一参数是否为deferred。
std::shared_future
futured对象中的get只能获取一次,为什么第二次get会得到一个异常,这个异常主要就是因为get函数的设计,是一个移动语义,相当于把里面的对象移动到另外一个内存中,再次调用的话就会报异常。如果有多个线程都想要获得get的结果,此时就需要使用std::shared_future。
std::shared_future也是一个类模板,此时get函数就不是转移数据,而是复制数据。
get多次的情况
int mythread3(std::shared_future<int>& tmp)
{
cout << "my thread start, and thread id is " << std::this_thread::get_id() << endl;
std::chrono::milliseconds dura(1000);
std::this_thread::sleep_for(dura);
cout << tmp.get() << endl;
cout << tmp.get() << endl;
cout << "my thread end, and thread id is " << std::this_thread::get_id() << endl; return 5;
}
在main函数中
cout << "Main thread start" << endl;
std::packaged_task<int(int)> mypt(mythread1);
std::thread t1(std::ref(mypt), 1);
t1.join();
std::future<int> result = mypt.get_future();
//std::shared_future<int> results(std::move(result)); //用std::move转成右值类型
std::shared_future<int> results(result.share());
std::thread t2(mythread3, std::ref(results));
t2.join();
原子操作、概念、基本用法
互斥量:用来在多线程编程中,保护共享数据:用一把锁把共享数据锁住,操作完毕之后再把锁打开。
一个线程读变量值,另外一个线程往变量中写值。
//读线程
int tmpvalue = atomvalue;
//写线程
atomvalue = 0;
读线程A和写线程B,如果写线程不断地往下写值,可能是读到新的值,也可能读到老的值,真正情况,会读到一个中间值,不可预料。即使一个简单的读或者幅值语句,也是分成很多步骤,一条语句会被拆成三四条汇编代码。
例子
int g_mycount = 0; //创建一个全局变量
void mythred_write() {
//线程入口函数
for (int i = 0; i < 1000000; i++) {
g_mycount++;
}
return;
}
main函数中
std::thread myobj1(mythred_write);
std::thread myobj2(mythred_write);
myobj1.join();
myobj2.join(); //两个线程执行完毕
cout << "加完的结果" << g_mycount << endl;
程序的执行结果,此时程序的执行结果并不是想象中的1,000,000+1,000,000:
返回值和我们想像中的不符合,线程在操作的时候不稳定,代码被拆分为多条汇编语言执行,加法的代码没有成功执行完,就被打断。可以用互斥量的知识来解决问题。
std::mutex mymutex; //线程入口函数修改
for (int i = 0; i < 1000000; i++) {
mymutex.lock();
g_mycount++;
mymutex.unlock();
}
除了用互斥量加锁的操作,用别的操作使得程序也达到同样的效果-->原子操作,无锁的多线程并发编程方式,或者也可以理解成原子操作是在多线程中不会被打断的程序执行片段。效率上而言,原子操作比互斥量效率上更胜一筹。有一点需要注意,互斥量,不仅仅加锁一行代码,原子操作一般针对的是一个变量,而不是一个代码段。在计算机中,原子操作是不可分割的操作,不可能出现中间状态。
std::atomic_int g_mycount = 0; //创建一个全局变量
atomic 是用来封装某给类型的值,可以定义成一个原子的全局量。
std::atomic<int> g_mycount = 0; //创建一个全局变量
像操作一个int对象一样来操作变量。根据范例,实用性为主,记住几个基本的用法范例即可。
心得体会:
1) std::atomic针对变量的赋值和判断,原子操作不能用于太复杂的操作。原子操作有用处,但是用处是有限的,在实际工作中,原子操作用的不太多,一般用于计数或者统计,累计发送出去了多少个数据包,接收了多少个数据包。实际工作中,如果有多个线程用来计数,可以考虑一下采用std::atomic变量。
2) 实际工作中,写商业代码,要谨慎行动,不太清楚这行代码有什么副作用,可以写一小段代码论证想法是否正确。或者干脆不使用。
C++并发与多线程学习笔记--future成员函数、shared_future、atomic的更多相关文章
- C++并发与多线程学习笔记--async、future、packaged_task、promise
async future packaged_task promise async std:async 是个函数,用来启动一个异步任务,启动起来一个异步任务之后,返回一个std::futre对象,启动一 ...
- C++并发与多线程学习笔记--互斥量、用法、死锁概念
互斥量(mutex)的基本概念 互斥量的用法 lock(), unlock() std::lock_guard类模板 死锁 死锁演示 死锁的一般解决方案 std::lock()函数模板 std::lo ...
- C++并发与多线程学习笔记--多线程数据共享问题
创建和等待多个线程 数据和共享问题分析 只读的数据 有读有写 其他案例 共享数据的保护案例代码 创建和等待多个线程 服务端后台开发就需要多个线程执行不同的任务.不同的线程执行不同任务,并返回执行结果. ...
- C++并发与多线程学习笔记--参数传递详解
传递临时对象 陷阱 总结 临时对象作为线程参数 线程id的概念 临时对象构造时的抓捕 成员函数指针做线程函数 传递临时对象作为线程参数 创建的工作线程不止一个,线程根据编号来确定工作内容.每个线程都需 ...
- C++并发与多线程学习笔记--unique_lock详解
unique_lock 取代lock_quard unique_lock 的第二个参数 std::adopt_lock std::try_to_lock std::defer_lock unique_ ...
- C++并发与多线程学习笔记--基本概念和实现
基本概念 并发 可执行程序.进程.线程 学习心得 并发的实现方法 多进程并发 多线程并发 总结 C++标准库 基本概念 (并发.进程.线程)区分C++初级编程和中高级编程 并发 两个或者更多的任务同时 ...
- C++并发与多线程学习笔记--atomic
std::atomic std::async std::atomic 一般atomic原子操作,针对++,--,+=,^=是支持的,其他结果可能不支持. 注意 std::atomic<int&g ...
- C++并发与多线程学习笔记--单例设计模式、共享数据分析
设计模式 共享数据分析 call_once 设计模式 开发程序中的一些特殊写法,这些写法和常规写法不一样,但是程序灵活,维护起来方便,别人接管起来,阅读代码的时候都会很痛苦.用设计模式理念写出来的代码 ...
- C++并发与多线程学习笔记--线程之间调度
condition_variable wait() notify_one notify_all condition_variable 条件变量的实际用途: 比如有两个线程A和B,在线程A中等待一个条件 ...
随机推荐
- full stack & front end
full stack & front end https://github.com/frank-lam/fullstack-tutorial https://github.com/haizli ...
- 云服务器Centos7部署Tomcat服务器
目录 部署Tomcat服务器 1.安装JDK1.8 2.安装与启动tomcat 配置安全组(8080端口) 参考文章 部署Tomcat服务器 1.安装JDK1.8 JDK下载地址:https://ww ...
- Simple: SQLite3 中文结巴分词插件
一年前开发 simple 分词器,实现了微信在两篇文章中描述的,基于 SQLite 支持中文和拼音的搜索方案.具体背景参见这篇文章.项目发布后受到了一些朋友的关注,后续也发布了一些改进,提升了项目易用 ...
- 线上CPU飙升100%问题排查
本文转载自线上CPU飙升100%问题排查 引子 对于互联网公司,线上CPU飙升的问题很常见(例如某个活动开始,流量突然飙升时),按照本文的步骤排查,基本1分钟即可搞定!特此整理排查方法一篇,供大家参考 ...
- Nifi组件脚本开发—ExecuteScript 使用指南(二)
Part 2 - FlowFile I/O 和 Error Handling flow File的IO NiFi 的 Flow files 由两个主要部件组成:attributes 和 content ...
- sql if else 用法
语法: case when 条件1 then 结果1 when 条件2 then 结果2 else 结果N end 可以有任意多个条件,如果没有默认的结果,最后的else也可以不写, select c ...
- python进阶(6)深拷贝和浅拷贝
深拷贝和浅拷贝 不管对于浅拷贝.还是深拷贝,针对不可变对象str.int.tuple(有点特殊).boolean,它的内存地址是不变的,拷贝的仅仅是值 import copy a = 1 b = co ...
- 框架进行时——SSM整合基础环境搭建
一.导入相关的依赖 1 <!--打war包--> 2 <packaging>war</packaging> 3 4 <!--版本锁定--> 5 < ...
- Dubbo之高级特性
Dubbo 注意当启动服务时,该服务会占用本机一个端口号,故在一台电脑启动多个服务时需要在配置文件中更占用本机的端口号 <!--服务占用本机的端口-当本机启动多个服务时须保持不同--> & ...
- ngx_http_image_filter_module使用
目录 安装 基本使用 示例 参数说明 参考链接:nginx官方文档 安装 ngx_http_image_filter_module一个官方模块,用于转换JPEG.GIF.PNG和WebP格式的图像. ...