C++11 (多线程)并发编程总结
| 线程
std::thread
- 创建std::thread,一般会绑定一个底层的线程。若该thread还绑定好函数对象,则即刻将该函数运行于thread的底层线程。
- 线程相关的很多默认是move语义,因为在常识中线程复制是很奇怪的行为。
- joinable():是否可以阻塞至该thread绑定的底层线程运行完毕(倘若该thread没有绑定底层线程等情况,则不可以join)
- join():本线程阻塞直至该thread的底层线程运行完毕。
- detach():该thread绑定的底层线程分离出来,任该底层线程继续运行(thread失去对该底层线程的控制)。
| 互斥变量
区域操作互斥:
为了避免多线程对共享变量的一段操作会发生冲突,引入了互斥体和锁。
std::mutex
- 互斥体,一般搭配锁使用,也可自己锁住自己(lock(),unlock())。
- 若互斥体被第二个锁请求锁住,则第二个锁所在线程被阻塞直至第一个锁解锁。
std::lock_guard
- 简单锁,构造时请求上锁,释放时解锁,性能耗费较低。适用区域的多线程互斥操作。
std::unique_lock
- 更多功能也更灵活的锁,随时可解锁或重新锁上(减少锁的粒度),性能耗费比前者高一点点。适用灵活的区域的多线程互斥操作。
原子操作互斥:
原子变量的意思就是单个最小的、不可分割的变量(例如一个int),原子操作则指单个极小的操作(例如一个自增操作)
C++的原子类封装了这种数据对象,使多线程对原子变量的访问不会造成竞争。(可以利用原子类可实现无锁设计)
std::atomic_flag
- 它是一个原子的布尔类型,可支持两种原子操作。(实际上mutex可用atomic_flag实现)
- test_and_set(): 如果atomic_flag对象被设置,则返回true; 如果atomic_flag对象未被设置,则设置之,返回false。
- clear():清除atomic_flag对象。
std::atomic<T>
- 对int, char, bool等基本数据类型进行原子性封装(其实是特化模板)。
- store():修改被封装的值。
- load(): 读取被封装的值
| 线程独立变量
thread_local
- 变量在每个线程各自独立(类似static),并在线程结束时释放。
| 条件变量
条件变量一般是用来实现多个线程的等待队列,即主线程通知(notify)有活干了,则等待队列中的其它线程就会被唤醒,开始干活。
std::condition_variable
- wait(std::unique_lock<std::mutex>& lock, Predicate pred = [](){return true;}):pred()为true时直接返回,pred()为false时,lock必须满足已被当前线程锁定的前提。执行原子地释放锁定,阻塞当前线程,并将其添加到等待*this的线程列表中。
- notify_one()/notify_all():激活某个或者所有等待的线程,被激活的线程重新获得锁。
虚假唤醒:
处于等待的添加变量可以通过notify_one/notify_all进行唤醒,调用函数进行信号的唤醒时,处于等待的条件变量会重新进行互斥锁的竞争。
没有得到互斥锁的线程就会发生等待转移(wait morphing),从等待信号量的队列中转移到等待互斥锁的队列中,一旦获取到互斥锁的所有权就会接着向下执行,
但是此时其他线程已经执行并重置了执行条件(例如一个活只需要两个线程来干,通知完两个线程后重置执行条件),这可能导致该线程执行引发未定义的错误。
//不能应对虚假唤醒 if(pred()){cv.wait(lock); }
//利用while重复判断执行条件,可以应对虚假唤醒 while(pred()){cv.wait(lock); }
//C++11提供了更方便的语法,将判断条件作为一个参数,实际上等价于前者 cv.wait(lock,pred);
| 提供方
std::promise<T>
- 构造时,产生一个未就绪的共享状态(包含存储的T值和是否就绪的状态)。可设置T值,并让状态变为ready。
- get_future():共享状态绑定到future对象。
- set_value():设置共享状态的T值,并让状态变为ready,则绑定的future对象可get()。
std::packaged_task<Func>
- 构造时绑定一个函数对象,也产生一个未就绪的共享状态。通过thread启动或者仿函数形式启动该函数对象。
- 但是相比promise,没有提供set_value()公用接口,而是当执行完绑定的函数对象,其执行结果返回值或所抛异常被存储于能通过 std::future 对象访问的共享状态中。
- get_future():共享状态绑定到future对象。
| 获取方
std::future<T>
- 用于访问共享状态(即获取值)。
- 当future的状态还不是ready时就调用一个绑定的promise, packaged_task等的析构函数,会在期望里存储一个异常。
- std::future有局限性,在很多线程等待时,只有一个线程能获取等待结果。
- share():分享同一个共享状态给另一个future
- wait():若共享状态不是ready,则阻塞直至ready。
- get():获得共享状态的值,若共享状态不是ready,则阻塞直至ready。
std::shared_future<T>
- 当需要多个线程等待相同的事件的结果(即多处访问同一个共享状态),需要用std::shared_future来替代std::future。
- shared_future与future类似,但shared_future可以拷贝、多个shared_future可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。
- shared_future可通过某个future对象隐式转换,或通过future::share()显示转换,无论哪种转换,被转换的那个future对象都会变为not-valid
| 异步(封装的异步操作)
std::async(std::launch::async | std::launch::deferred, Func, Args...)
- 异步执行一个函数,其函数执行完后的返还值绑定给使用std::async的futrue(其实是封装了thread,packged_task的功能,使异步执行一个任务更为方便)。
- 若用创建std::thread执行异步行为,硬件底层线程可能不足,产生错误。而std::async将这些底层细节掩盖住,如果使用默认参数则与标准库的线程管理组件一起承担线程创建和销毁、避免过载、负责均衡的责任。
- 所以尽量使用以任务为驱动的async操作设计,而不是以线程为驱动的thread设计。
- std::async中的第一个参数是启动策略,它控制std::async的异步行为,我们可以用三种不同的启动策略来创建std::async:
std::launch::async参数 保证异步行为,即传递函数将在单独的线程中执行。
std::launch::deferred参数 当其他线程调用get()/wait()来访问共享状态时,将调用非异步行为。
std::launch::async | std::launch::deferred参数 是默认行为。有了这个启动策略,它可以异步运行或不运行,这取决于系统的负载。
///使用示例 std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data1"); std::string data1 = resultDromDB.get();
转载于:https://www.cnblogs.com/KillerAery/p/9574410.html
C++11 (多线程)并发编程总结相关推荐
- Java多线程并发编程--Java并发包(JUC)
Java多线程并发–Java并发包(JUC) 前言 前一篇文章中,笔者已经介绍了Java多线程的一些基础知识,但是想要成为一名中高级Java程序员还必须懂得Java并发包(JUC)的知识点,而且JUC ...
- python 多线程并发编程(生产者、消费者模式),边读图像,边处理图像,处理完后保存图像实现提高处理效率
文章目录 需求 实现 先导入本次需要用到的包 一些辅助函数 如下函数是得到指定后缀的文件 如下的函数一个是读图像,一个是把RGB转成BGR 下面是主要的几个处理函数 在上面几个函数构建对应的处理函数 ...
- Java 多线程 并发编程
转载自 Java 多线程 并发编程 一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进 ...
- Java多线程并发编程
一.线程池 1.1.什么是线程池 线程池是一种多线程的处理方式,利用已有线程对象继续服务新的任务(按照一定的执行策略),而不是频繁地创建销毁线程对象,由此提高服务的吞吐能力,减少CPU的闲置时间.具体 ...
- 网易云课堂微专业--Java高级开发工程师--多线程并发编程--学习笔记(二)
文章目录 第一章 多线程并发编程 第二节 线程安全问题 1.2.1 线程安全之可见性问题 多线程中的问题 从内存结构到内存模型 工作内存缓存 指令重排序 内存模型的含义 Shared Variable ...
- 【2022】多线程并发编程面试真题
文章目录 4. 多线程 4.1 创建线程有哪几种方式? 4.2 说说Thread类的常用方法 4.3 run()和start()有什么区别? 4.4 线程是否可以重复启动,会有什么后果? 4.5 介绍 ...
- JUC多线程并发编程
1.什么是JUC 源码+官方文档 JUC是 java util concurrent 面试高频问JUC~! java.util 是Java的一个工具包~ 业务:普通的线程代码 Thread Runna ...
- java线程钥匙_Java多线程并发编程/锁的理解
一.前言 最近项目遇到多线程并发的情景(并发抢单&恢复库存并行),代码在正常情况下运行没有什么问题,在高并发压测下会出现:库存超发/总库存与sku库存对不上等各种问题. 在运用了 限流/加锁等 ...
- 【收藏】Java多线程/并发编程大合集
(一).[Java并发编程]并发编程大合集-兰亭风雨 [Java并发编程]实现多线程的两种方法 [Java并发编程]线程的中断 [Java并发编程]正确挂起.恢复.终止线程 [ ...
最新文章
- 安卓 画板 学习笔记
- Effective C++ 类与函数设计和申明
- 30天敏捷结果(19):你在为谁做事?
- mos 多路模拟电子开关_【原创】单火线智能开关技术介绍及分析
- wxWidgets:更新到最新版本的 wxWidgets
- oracle取本月最后一天是星期几_oracle SQL语句取本周本月本年的数据
- BZOJ1576: [Usaco2009 Jan]安全路经Travel(树链剖分)
- Oracle正则表达式函数:regexp_like、regexp_substr、regexp_instr、regexp_replace
- 【转】Ubuntu 修改hosts
- java、Android 数组转集合
- wifisetting.java_Wifi 笔记 | 启动流程
- 对 带头结点的单链表 的操作
- 牛客14392 猴子吃香蕉
- python批量运行cmd_python 批量ssh并执行命令
- 如何用Sql更新默认值
- 赢在电子商务网站制作与营销:B2C版
- oracle ebs工单入库,ORACLE-EBS_库存功能点操作模块.doc
- 【Kotlin -- 知识点】学习资料
- html实现多选框传值,解决Django中checkbox复选框的传值问题
- Java中类和对象的区别