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中等待一个条件 ...
随机推荐
- webpack & chunkhash
webpack & chunkhash https://webpack.js.org/configuration/output/#outputchunkfilename https://web ...
- windows 隐藏desktop.ini文件
原文 在文件夹选项>查看 勾选"隐藏受保护的操作系统文件(推荐)"选项
- Codeforces 1485F Copy or Prefix Sum
题目链接 点我跳转 题目大意 给定一个长度为 \(N\) 的序列 \(bi\) 问有多少个长度为 \(N\) 的序列 \(a\) 使得 \(b[i] = a[i]\) 或 \(b[i] = ∑a[j] ...
- django学习-7.html模板中include标签使用场景
1.前言 假设一个公司A有一个网站B,且网站B有5个不同的页面分别为C1,C2,C3,C4,C5. 那么,我们在打开这5个不同页面后去查看页面的整体内容,会发现每个页面的顶部内容.底部内容都一模一样. ...
- 使用docker mediawiki,搭建网页wiki
我只是想做一个大家都能访问的wiki,用于成员间共享和维护一些文档.找到了docker的mediawiki,这里记录一下我怎么搭的吧. 首先,如果你在一个局域网里,有公用的可以访问的服务器,那可以直接 ...
- Java 动态调试技术原理及实践
本文转载自Java 动态调试技术原理及实践 导语 断点调试是我们最常使用的调试手段,它可以获取到方法执行过程中的变量信息,并可以观察到方法的执行路径.但断点调试会在断点位置停顿,使得整个应用停止响应. ...
- 【OI向】快速傅里叶变换(Fast Fourier Transform)
[OI向]快速傅里叶变换(Fast Fourier Transform) FFT的作用 在学习一项算法之前,我们总该关心这个算法究竟是为了干什么. (以下应用只针对OI) 一句话:求多项式 ...
- Vue学习笔记-rest_framework_jwt 学习
一 使用环境 开发系统: windows 后端IDE: PyCharm 前端IDE: VSCode 数据库: msyql,navicat 编程语言: python3.7 (Windows x86- ...
- Vuex理解与使用
1.Vuex是什么 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式,用于管理页面的数据状态.提供统一数据操作的生态系统.在组件中可以任意获取仓库中的数据.和Vuex类似的还有redux ...
- MySQL深入研究--学习总结(1)
前言 本文是笔者学习"林晓斌"老师的<MySQL实战45讲>过程中的,对知识点的总结归纳以及对问题的思考记录,课程18年11月就出了,当时连载形式,我就上班途中一边开车 ...