C++11并发编程个人小结
thread_local变量在每个线程第一次执行到时初始化(类似static),并在每个线程各自累加,并在线程结束时释放。
std::condition_variable::
wait(std::unique_lock<std::mutex>& lock, Predicate pred);
pred为true时直接返回;
pred为false时,lock必须满足已被当前线程锁定的前提。执行原子地释放锁定,阻塞当前线程,并将其添加到等待*this的线程列表中。
void notify_one();
激活某个等待*this的线程,被激活的线程重新获得锁。
async:能够保留线程的返回值。启动一个线程,或在期望等待时同步任务.
std::launch::async表明函数必须在其所在的独立线程上执行
std::launch::defered表明函数调用被延迟到wait()或get()函数调用时才执行
如果两者都行,取决于实现;未声明则未定义。
####################
线程相关的很多=默认是move语义。
thread:.join(), .detach(), .swap()
mutex:mutex, lock_guard, unique_lock等, .lock(), .unlock()
condition_variable:.notify_one(), .wait()//实现等待队列
future:
async: 类似于thread, 能保留返回值,可启动一个线程,或在期望等待时同步任务.
std::launch::async表明函数必须在其所在的独立线程上执行
std::launch::defered表明函数调用被延迟到wait()或get()函数调用时才执行
如果两者都行,取决于实现;未声明则未定义。
packaged_task: 构造时绑定一个函数对象,.get_future()将返回值绑定到future,再执行task函数(或传递到std::thread对象中可作为线程函数)
promise: .getfuture(), 绑定到future; 执行.set_value()后, 绑定的future可.get(), 否则.get()一直被阻塞直到.set_value()执行。
future: .share(), .wait(), .get()
当future的状态还不是就绪时就调用绑定的promise, packaged_task等的析构函数,会在期望里存储一个异常。
std::future有局限性,在很多线程等待时,只有一个线程能获取等待结果。
当多个线程需要等待相同的事件的结果,需要用shared_future来替代future。
shared_future与future类似,但shared_future可以拷贝、多个shared_future可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。
shared_future可通过某个future对象隐式转换,或通过future::share()显示转换,无论哪种转换,被转换的那个future对象都会变为not-valid.
atomic:
is_lock_free: 如果某个对象满足 lock-free 特性,在多个线程访问该对象时不会导致线程阻塞。
(其实看我们那副图就是说你的各个线程不会互相阻塞,
那么你的程序才能成为lock free的。
像我们平常用的互斥锁,当有线程获得锁,
其他线程就被阻塞掉了,
这里的问题就是如果获得锁的线程挂掉了,
而且锁也没有释放,那么整个程序其实就被block在那了,
而如果程序是lock free的那么即使有线程挂掉,
也不影响整个程序继续向下进行,
也就是系统在整体上而言是一直前进的。
大概系统内部自有一套判断机制)
store: 修改被封装的值
load: 读取被封装的值
内存模型:
要想写出高性能的多线程程序必须理解内存模型,
因为编译器会给你的程序做优化(如指令重排等),
CPU为了提升性能也有多发射和乱序执行,
因此程序在最终执行时并不会按照你之前的原始代码顺序来执行,
所以内存模型是程序员、编译器,CPU 之间的契约,遵守契约后大家就各自做优化,从而尽可能提高程序的性能。
_relaxed: 不对执行顺序做任何保证
_acquire: 本线程中,所有后续的读操作必须在本原子操作完成后执行
_release: 本线程中,所有之前的写操作完成后才能执行本原子操作
_acq_rel: 同时包含acquire和release
_consume: 本线程中,所有后续的有关本原子类型的操作,必须在本原子操作完成后执行
_seq_cst: 全部存取按顺序执行
=========================================================
PV原语: 信号量s, 除一个整数值s.cound, 还有一个进程等待队列s.queue, 是阻塞在该信号上的进程。
信号量的值为非负值表示当前空闲资源数, 负值表示当前阻塞在该信号上的进程。
P(Semaphore s) {--s.count; if(s.count < 0) 阻塞调用进程;}
V(Semaphore s) {++s.count; if(s.count <= 0) 唤醒某一进程;}
互斥量mutex就是count = 1的PV原语。
如何实现同步?考虑进程的DAG图, A进程的某个节点a有指向B进程的某个节点b, 那么相当于一开始b处于阻塞态, A进程到a处执行V原语释放资源。如公交司机开车停车与售票员开门关门。
经典算法
生产者与消费者:(以下代码每个生产者生产数量与每个消费者消费数量相同)
/***************
首先你要有一个生产者和消费者公用的Buffer, 它要求用mutex和condition variable来实现锁
一个互斥量, 互斥地访问buffer, 多个消费者和生产者每个消费和生产的数量相同。
本写法本质为单消费者单生产者。
正确写法:
单生产者单消费者: 设置一个互斥量mtx, 互斥地访问buffer; 两个条件变量表示buffer满或空
多生产者单消费者: 再规定总生产量为count_produce, 多生产者互斥地访问count_produce
多生产者单消费者: 再规定总消费量为count_consume, 多消费者互斥地访问count_consume
多生产者单消费者: 再规定总生产量为count_produce, 多生产者互斥地访问count_produce, 规定总消费量为count_consume(count_consume = count_produce), 多消费者互斥地访问count_consume
***************/
#include <bits/stdc++.h>
struct BoundedBuffer {
int* buffer;
int capacity; int front;
int rear;
int count; std::mutex lock; std::condition_variable not_full;
std::condition_variable not_empty; BoundedBuffer(int capacity) : capacity(capacity), front(), rear(), count() {
buffer = new int[capacity];
}
~BoundedBuffer(){
delete[] buffer;
}
void deposit(int data){
std::unique_lock<std::mutex> l(lock);
not_full.wait(l, [this](){return count != capacity; });
buffer[rear] = data;
rear = (rear + ) % capacity;
++count;
not_empty.notify_one();
}
int fetch(){
std::unique_lock<std::mutex> l(lock);
not_empty.wait(l, [this](){return count != ; });
int result = buffer[front];
front = (front + ) % capacity;
--count;
not_full.notify_one();
return result;
}
}; void consumer(int id, BoundedBuffer& buffer){
for(int i = ; i < ; ++i){
int value = buffer.fetch();
std::cout << "Consumer " << id << " fetched " << value << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds());
}
} void producer(int id, BoundedBuffer& buffer){
for(int i = ; i < ; ++i){
buffer.deposit(i);
std::cout << "Produced " << id << " produced " << i << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds());
}
} int main(){
BoundedBuffer buffer(); std::thread c1(consumer, , std::ref(buffer));
std::thread c2(consumer, , std::ref(buffer));
std::thread c3(consumer, , std::ref(buffer));
std::thread p1(producer, , std::ref(buffer));
std::thread p2(producer, , std::ref(buffer)); c1.join();
c2.join();
c3.join();
p1.join();
p2.join(); return ;
}
读者写者问题: 参考链接
哲学家进餐问题: 1.最多允许四个人同时在进餐 2.当两边筷子都可用时再去拿 3.每次先拿奇数号的筷子再拿偶数号的筷子 4.分成三种状态, 思考, 饥饿, 进餐, 并且一次拿一双
C++11并发编程个人小结的更多相关文章
- C++11 并发编程库
C++11 并发编程 C++11 新标准中引入了几个头文件来支持多线程编程,他们分别是: <atomic>:该头文主要声明了两个类, std::atomic 和 std::atomic_f ...
- C++11 并发编程基础(一):并发、并行与C++多线程
正文 C++11标准在标准库中为多线程提供了组件,这意味着使用C++编写与平台无关的多线程程序成为可能,而C++程序的可移植性也得到了有力的保证.另外,并发编程可提高应用的性能,这对对性能锱铢必较的C ...
- C++11并发编程:多线程std::thread
一:概述 C++11引入了thread类,大大降低了多线程使用的复杂度,原先使用多线程只能用系统的API,无法解决跨平台问题,一套代码平台移植,对应多线程代码也必须要修改.现在在C++11中只需使用语 ...
- C++11并发编程:原子操作atomic
一:概述 项目中经常用遇到多线程操作共享数据问题,常用的处理方式是对共享数据进行加锁,如果多线程操作共享变量也同样采用这种方式. 为什么要对共享变量加锁或使用原子操作?如两个线程操作同一变量过程中,一 ...
- C++11并发编程:async,future,packaged_task,promise
一:async std::async:用于创建异步任务,可以代替创建线程,函数原型:async(std::launch::async | std::launch::deferred, f, args. ...
- C++11并发编程1------并发介绍
也许有人会觉得多线程和并发难用,复杂,还会让代码出现各种各样的问题,不过,其实它是一个强有力的工具,能让程序充分利用硬件资源,让程序运行得更快. 何谓并发: 两个或更多独立得活动同时发生.计算机中就是 ...
- C++11并发编程实战 免费书籍
C++11 博客http://www.cnblogs.com/haippy/p/3284540.html 网上推荐的C++多线程基本都是C++ Concurrency in Action 英文版的,中 ...
- 11 并发编程-(线程)-信号量&Event&定时器
1.信号量(本质也是一把锁)Semaphore模块 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行, 信号量同一时间可以有5个任务拿到锁去执行, 如果说互斥锁是合租 ...
- Windows:C++11并发编程-条件变量(condition_variable)详解
<condition_variable >头文件主要包含了与条件变量相关的类和函数.相关的类包括 std::condition_variable和 std::condition_varia ...
随机推荐
- 报错:Cannot create PoolableConnectionFactory (The server time zone value 'CST' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverT
报错:Cannot create PoolableConnectionFactory (The server time zone value 'CST' is unrecognized or repr ...
- Docker Manager for Docker Swarm deploy
一.Swarm概述 Swarm是Docker公司在2014年12月初发布的一套较为简单的工具,用来管理Docker集群,它将一群Docker宿主机变成一个单一的,虚拟的主机.Swarm使用标准的Doc ...
- 小米6x抓包小程序https请求
1. charles安装证书,手机设置代理等这里不多讲了, 请进入下面链接查看详细 https://blog.csdn.net/manypeng/article/details/79475870 2. ...
- OGG 问题
1.启动复制时报 "ERROR OGG-15050 Oracle GoldenGate Delivery, l***.prm: Error loading Java VM runtime l ...
- C++ 函数 引用
一.引用的概念 引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样.引用的声明方法: 类型标识符 &引用名 = 目标变量名: 为一个变量起一个别名.假如有一个变量a,想给 ...
- 第31次Scrum会议(11/19)【欢迎来怼】
一.小组信息 队名:欢迎来怼 小组成员 队长:田继平 成员:李圆圆,葛美义,王伟东,姜珊,邵朔,阚博文 小组照片 二.开会信息 时间:2017/11/19 17:05~17:34,总计29min. 地 ...
- BugPhobia准备篇章:Beta阶段前后端接口文档
0x00:序言 Two strangers fell in love, Only one knows it wasn’t by chance. To the searching tags, you m ...
- Python对list去重
Python对list去重 方法一 新建新的列表,利用not in命令去重.这种方法看起来不够简便,但是保留了原列表中的顺序.代码如下: list1 = [1,2,3,4,1,1,2,5,4,3] l ...
- 第一个Sprint冲刺成果
组长:李咏江,组员:叶煜稳,谢洪跃,周伟雄 进程:第一个算法功能完成
- 结对作业-四则运算GUI
目录: 一.项目地址二.PSP三.接口设计四.计算模块接口的设计与实现过程五.计算模块接口部分的性能改进六.计算模块部分单元测试展示七.计算模块部分异常处理说明八.界面模块的详细设计过程九.界面模块与 ...