当前位置: 首页 > news >正文

c++11扩展(c++11并发库)

文章目录

  • 1、并发支持库
    • 1.1 thread库
    • 2. this_thread
    • 3. mutex
    • 4. lock_guard
    • 5. unique_lock
    • 6. lock和try_lock
    • 7. call_once
    • 8. atomic
    • 9. condition_variable
  • 总结

1、并发支持库

1.1 thread库

a、thread库文档https://legacy.cplusplus.com/reference/thread/thread/thread/
b、thread库是对各个系统的线程库进行封装,如Linux下的pthera库和windows下Thread库等,所以c++11thread库第一个特点:可以跨平台,第二个特点:Linux和windows下提供的线程库是面向过程的,c++11thread是库面向对象的,并且融入了c++11语言特点,如右值引用的移动语义,可变模版参数等。
在这里插入图片描述

c、下⾯线程创建这⾥有4个构造函数,⽇常最常⽤的是第2个,他⽀持传⼀个可调⽤对象和参数即可,相⽐pthread_create⽽⾔,这⾥不再局限于只传递函数指针,其次就是参数传递也更⽅便,pthread_create调⽤时,要传递多个参数需要打包成⼀个结构体,传结构体对象的指针过去。
d、 另外也可以⽤第1个和第4个配合来创建线程,我们可以把右值线程对象移动构造或者移动赋值给另⼀个线程对象。
e、 第3个可以看到线程对象是不⽀持拷⻉的。
f、 join是主线程结束前需要阻塞等待创建的从线程,否则主线程结束,进程就结束了,从线程可能还在运⾏就被强⾏终⽌了。
j、 class thread::id是⼀个thread的内部类⽤来表⽰线程id,⽀持⽐较⼤⼩,流插⼊和提取,通过特化hash仿函数做unordered_map和unordered_set的id等。底层的⻆度看thread本质还是封装各个平台的线程库接⼝。各个平台的线程id表⽰类型不同,所以只能⽤⼀个类来进⾏封装。线程对象可以通过get_id获取线程id,在执⾏体内可以通过this_thread::get_id()获取线程id。

default (1)
thread() noexcept;
initialization (2)
template <class Fn, class... Args>
explicit thread (Fn&& fn, Args&&... args);
copy [deleted] (3)
thread (const thread&) = delete;
copy [deleted] (3)
thread& operator= (const thread&) = delete;
move (4)
thread (thread&& x) noexcept;
move (4)
thread& operator= (thread&& rhs) noexcept;
// pthread库
int pthread_create(pthread_t *tidp,const pthread_attr_t *attr, void *(*start_rtn)(void*), void *arg);
// windows线程创建API
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,//SD
SIZE_T dwStackSize,//initialstacksize
LPTHREAD_START_ROUTINE lpStartAddress,//threadfunction
LPVOID lpParameter,//threadargument
DWORD dwCreationFlags,//creationoption
LPDWORD lpThreadId//threadidentifier
)
void join();#include<iostream>
#include<thread>
#include<vector>
#include<mutex>
using namespace std;
void Print(int n, int i)
{
for (; i < n; i++)
{
cout << this_thread::get_id() <<":" << i << endl;//调用全局函数,获取当前函数的id
}
cout << endl;
}
//多核
int main()
{
thread t1(Print, 10, 0);
thread t2(Print, 20, 10);
// 获取线程id
//cout << t1.get_id() << endl;
//cout << t2.get_id() << endl;
t1.join();//从线程结束
t2.join();
// 获取当前运⾏线程id
cout << this_thread::get_id() << endl;
return 0;
}

2. this_thread

this_thread官方文档
• this_thread是⼀个命名空间,主要封装了线程相关的4个全局接⼝函数。
• get_id是当前执⾏线程的线程id。
• yield是主动让出当前线程的执⾏权,让其他线程先执⾏。此函数的确切⾏为依赖于实现,特别是取决于使⽤中的 OS 调度器机制和系统状态。例如,先进先出实时调度器(Linux 的 SCHED_FIFO)会挂起当前线程并将它放到准备运⾏的同优先级线程的队列尾,⽽若⽆其他线程在同优先级,则 yield ⽆效果。
• sleep_for阻塞当前线程执⾏,⾄少 经过指定的 sleep_duration。因为调度或资源争议延迟,此函数可能阻塞⻓于 sleep_duration。
• sleep_until阻塞当前线程的执⾏,直⾄抵达指定的 sleep_time。函数可能会因为调度或资源纠纷延迟⽽阻塞到 sleep_time 之后的某个时间点。
https://legacy.cplusplus.com/reference/chrono/ chrono是⼀个计时相关的类型。
https://legacy.cplusplus.com/reference/chrono/duration/是⽤来管理⼀个相对时间段的类。
https://legacy.cplusplus.com/reference/chrono/time_point/是⽤来管理⼀个绝对时间点的类。

template <class Clock, class Duration>
void sleep_until (const chrono::time_point<Clock,Duration>& abs_time);
template <class Rep, class Period>
void sleep_for (const chrono::duration<Rep,Period>& rel_time);
// this_thread::sleep_for example
#include <iostream> // std::cout, std::endl
#include <thread> // std::this_thread::sleep_for
#include <chrono> // std::chrono::seconds
int main()
{
std::cout << "countdown:\n";
for (int i=10; i>0; --i) {
std::cout << i << std::endl;
std::this_thread::sleep_for (std::chrono::seconds(1));//休眠1秒
}
std::cout << "Lift off!\n";
return 0;
}
// this_thread::sleep_for example
#include <iostream> // std::cout
#include <iomanip> // std::put_time
#include <thread> // std::this_thread::sleep_until
#include <chrono> // std::chrono::system_clock
#include <ctime> // std::time_t, std::tm, std::localtime, std::mktime
int main()
{
using std::chrono::system_clock;
std::time_t tt = system_clock::to_time_t (system_clock::now());
struct std::tm * ptm = std::localtime(&tt);
std::cout << "Current time: " << std::put_time(ptm,"%X") << '\n';
std::cout << "Waiting for the next minute to begin...\n";
++ptm->tm_min; ptm->tm_sec=0;
std::this_thread::sleep_until (system_clock::from_time_t (mktime(ptm)));
std::cout << std::put_time(ptm,"%X") << " reached!\n";
return 0;
}
int main ()
{
using namespace std::chrono;
duration<int,std::ratio<60*60*24> > one_day (1);
system_clock::time_point today = system_clock::now();
system_clock::time_point tomorrow = today + one_day;
time_t tt;
tt = system_clock::to_time_t ( today );
std::cout << "today is: " << ctime(&tt);
tt = system_clock::to_time_t ( tomorrow );
std::cout << "tomorrow will be: " << ctime(&tt);
return 0;
}

3. mutex

https://legacy.cplusplus.com/reference/mutex/
• mutex是封装的互斥锁的类,⽤于保护临界区的共享数据。mutex主要提供lock和unlock两个接⼝函数。 mutex 提供排他性⾮递归所有权语义:(1)调⽤⽅线程从它成功调⽤ lock或 try_lock 开始,到它调⽤ unlock 为⽌占有 mutex。(2)线程占有 mutex 时,其他线程如果试图要求 mutex 的所有权,那么就会阻塞(对于 lock 的调⽤), 对于 try_lock就会返回false 。
锁不能被拷贝,因为锁万一里面有线程就会拷不到。
• 如果 mutex 在仍为任何线程所占有时即被销毁,或在占有 mutex 时线程终⽌,那么⾏为未定义。
• ⽰例1代码展⽰了mutex的使⽤,其实如果线程对象传参给可调⽤对象时,使⽤引⽤⽅式传参,实参位置需要加上ref(obj)的⽅式,主要原因是thread本质还是系统库提供的线程API的封装,thread构造取到参数包以后,要调⽤创建线程的API,还是需要将参数包打包成⼀个结构体传参过去,那么打包成结构体时,参考包对象就会拷⻉给结构体对象,使⽤ref传参的参数,会让结构体中的对应参数成员类型推导为引⽤,这样才能实现引⽤传参,⽰例2代码截取了vs2019下thread库中的部分源码帮助理解。https://legacy.cplusplus.com/reference/functional/ref/?kw=ref
• time_mutex跟mutex完全类似,只是额外提供try_lock_for和try_lock_untile的接⼝,这两个接⼝跟try_lock类似,只是他不会⻢上返回,⽽是直接进⼊阻塞,直到时间条件到了或者解锁了就会唤醒试图获取锁资源。
• recursive_mutex 跟mutex完全类似,recursive_mutex 提供排他性递归所有权语义:
(1)调⽤⽅线程在从它成功调⽤ lock 或 try_lock 开始的时期⾥占有 recursive_mutex。此时期之内,线程可以进⾏对 lock 或 try_lock 的附加调⽤。所有权的时期在线程进⾏匹配次数的 unlock 调⽤时结束。
(2)线程占有 recursive_mutex 时,若其他所有线程试图要求 recursive_mutex 的所有权,则它们将阻塞(对于调⽤ lock)或收到 false 返回值(对于调⽤ try_lock)
在这里插入图片描述
在这里插入图片描述

// ⽰例1:
#include <iostream>
#include <chrono>
#include <thread>
#include <mutex>
using namespace std;
void Print(int n, int& rx, mutex& rmtx)
{
//锁放在外面,效率高,因为是t1全部走完了,才是t2走
//锁放在循环里面,效率低,会发生阻塞轮替,产生消耗。
rmtx.lock();
for (int i = 0; i < n; i++)
{
// t1 t2
++rx;
}
rmtx.unlock();
}
int main()
{
int x = 0;
mutex mtx;
// 这⾥必须要⽤ref()传参,现成中拿到的才是x和mtx的引⽤,具体原因需要看下⾯thread源码中的分析
// https://legacy.cplusplus.com/reference/functional/ref/?kw=ref
thread t1(Print, 1000000, ref(x), ref(mtx));
thread t2(Print, 2000000, ref(x), ref(mtx));
t1.join();
t2.join();
cout << x << endl;
return 0;
}
int main()
{
int x = 0;
mutex mtx;
// 将上⾯的代码改成使⽤lambda捕获外层的对象,也就可以不⽤传参数,间接解决了上⾯的问题
auto Print = [&x, &mtx](size_t n) {
mtx.lock();
for (size_t i = 0; i < n; i++)
{
++x;
}
mtx.unlock();
};
thread t1(Print, 1000000);
thread t2(Print, 
http://www.dtcms.com/a/335262.html

相关文章:

  • 在职老D渗透日记day18:sqli-labs靶场通关(第26关)get报错注入 过滤or和and基础上又过滤了空格和注释符 ‘闭合 手动注入
  • echarts 画一个饼图,并且外围有一个旋转动画
  • linux下程序运行一段时间无端崩溃/被杀死,或者内存占用一直增大。linux的坑
  • 11.web api 2
  • 模式匹配自动机全面理论分析
  • AI短视频爆火?记录AIGC在影视制作场景的实践教程
  • 大模拟 Major
  • 随机整数列表处理:偶数索引降序排序
  • jd-hotkey探测热点key
  • 流量分析服务一审构成非法经营罪二审改判:数据服务的法律边界
  • 电路方案分析(二十二)适用于音频应用的25-50W反激电源方案
  • ethernet_input到应用层处理简单分析
  • 5 索引的操作
  • K8s核心组件全解析
  • 如何使用嵌入模型创建本地知识库Demo
  • 三、memblock 内存分配器
  • 深入理解文件硬链接、软链接与引用计数的那些事
  • 机器学习相关算法:回溯算法 贪心算法 回归算法(线性回归) 算法超参数 多项式时间 朴素贝叶斯分类算法
  • 超详细yolo8/11-pose人体姿态全流程概述:配置环境、数据标注、训练、验证/预测、onnx部署(c++/python)详解
  • 8.16、8.17 JavaWeb(MyBatis P116-P134)
  • 【网络与爬虫 00】试读
  • lcx、netcat、powercat--安装、使用
  • 【RH134知识点问答题】第 10 章:控制启动过程
  • 深入浅出OpenGL的glDrawArray函数
  • 设计索引的原则有哪些?
  • 数据结构初阶(16)排序算法——归并排序
  • w嵌入式分享合集66
  • 开发一款多商户电商APP要多久?功能拆解与源码技术落地方案
  • vulhub-driftingblues9缓冲区溢出攻击提权
  • 写一个linux脚本,要求实现查找9010端口,如果端口存在则kill,否则不处理,返回对应的提示