多线程c++

目录

1.join和detach区别

2.lock_guard和unique_lock 

3.原子操作

4.条件变量condition_variable 

5.future 和 promise 


1.join和detach区别

①不使用join和detach

#include <iostream>
#include <thread>
#include <windows.h>using namespace std;void t1()  //普通的函数,用来执行线程
{for (int i = 0; i < 10; ++i){cout <<" t1 :"<< i <<"  ";_sleep(1);}
}
void t2()
{for (int i = 11; i < 20; ++i){cout <<" t2 :"<< i <<"  ";_sleep(1);}
}
int main()
{thread th1(t1);  //实例化一个线程对象th1,使用函数t1构造,然后该线程就开始执行了(t1())thread th2(t2);cout << "here is main\n\n";system("pause");return 0;
}

每次输出的结果都不一样

②使用join

#include <iostream>
#include <thread>
#include <windows.h>using namespace std;void t1()  //普通的函数,用来执行线程
{for (int i = 0; i < 10; ++i){cout <<" t1 :"<< i <<"  ";_sleep(1);}
}
void t2()
{for (int i = 11; i < 20; ++i){cout <<" t2 :"<< i <<"  ";_sleep(1);}
}
int main()
{thread th1(t1);  //实例化一个线程对象th1,使用函数t1构造,然后该线程就开始执行了(t1())thread th2(t2);th1.join(); // 必须将线程join或者detach 等待子线程结束主进程才可以退出th2.join(); cout << "here is main\n\n";system("pause");return 0;
}

join会将主线程和子线程th1 th2分离,子线程执行完才会执行主线程。(两个子线程输出每次仍不一样)

③使用detach

#include <iostream>
#include <thread>#include <windows.h>using namespace std;void t1()  //普通的函数,用来执行线程
{for (int i = 0; i < 10; ++i){cout <<" t1 :"<< i <<"  ";_sleep(1);}}
void t2()
{for (int i = 11; i < 20; ++i){cout <<" t2 :"<< i <<"  ";_sleep(1);}
}
int main()
{thread th1(t1);  //实例化一个线程对象th1,使用函数t1构造,然后该线程就开始执行了(t1())thread th2(t2);th1.detach();th2.detach();cout << "here is main\n\n";system("pause");return 0;
}

detach也会将主线程和子线程th1 th2分离,但是主线程执行过程子线程仍会执行

总结:

1. join会阻塞当前的线程,直到运行的线程结束。(例如上面第二段代码主线程被阻塞,直到子线程执行完才会执行join之后的主线程代码)

2.detach从线程对象中分离出执行线程,允许线程独立的执行。(上面第三段代码,主线程和两个子线程独立的执行)

参考文章

2.lock_guard和unique_lock 

线程的使用一定需要搭配锁mutex,它是用来保证线程同步的,防止不同的线程同时操作同一个共享数据。

①使用mutex,使用lock函数上锁,unlock解锁

#include <iostream>
#include <thread>
#include <mutex>
#include <stdlib.h>int cnt = 20;
std::mutex m;
void t1()
{while (cnt > 0){    m.lock();if (cnt > 0){--cnt;std::cout << cnt << std::endl;}m.unlock();}
}
void t2()
{while (cnt < 20){m.lock();if (cnt < 20){++cnt;std::cout << cnt << std::endl;}m.unlock();}
}int main(void)
{std::thread th1(t1);std::thread th2(t2);th1.join();    //等待t1退出th2.join();    //等待t2退出std::cout << "here is the main()" << std::endl;system("pause");return 0;
}

②使用lock_guard

使用mutex比较繁琐,需要上锁和解锁,c++提供了lock_guard,它是基于作用域的,能够自解锁,当该对象创建时,它会像m.lock()一样获得互斥锁,当生命周期结束时,它会自动析构(unlock),不会因为某个线程异常退出而影响其他线程。

#include <iostream>
#include <thread>
#include <mutex>
#include <stdlib.h>int cnt = 20;
std::mutex m;
void t1()
{while (cnt > 0){    std::lock_guard<std::mutex> lockGuard(m);// m.lock();if (cnt > 0){--cnt;std::cout << cnt << std::endl;}// m.unlock();}
}
void t2()
{while (cnt < 20){std::lock_guard<std::mutex> lockGuard(m);// m.lock();if (cnt < 20){++cnt;std::cout << cnt << std::endl;}// m.unlock();}
}int main(void)
{std::thread th1(t1);std::thread th2(t2);th1.join();    //等待t1退出th2.join();    //等待t2退出std::cout << "here is the main()" << std::endl;system("pause");return 0;
}

③使用unique_lock

lock_guard有个很大的缺陷,在定义lock_guard的地方会调用构造函数加锁,在离开定义域的话lock_guard就会被销毁,调用析构函数解锁。这就产生了一个问题,如果这个定义域范围很大的话,那么锁的粒度就很大,很大程序上会影响效率。

因此提出了unique_lock,这个会在构造函数加锁,然后可以利用unique.unlock()来解锁,所以当锁的颗粒度太多的时候,可以利用这个来解锁,而析构的时候会判断当前锁的状态来决定是否解锁,如果当前状态已经是解锁状态了,那么就不会再次解锁,而如果当前状态是加锁状态,就会自动调用unique.unlock()来解锁。而lock_guard在析构的时候一定会解锁,也没有中途解锁的功能。

参考文章

3.原子操作

原子性操作库(atomic)是C++11中新增的标准库,它提供了一种线程安全的方式来访问和修改共享变量,避免了数据竞争的问题。在多线程程序中,如果多个线程同时对同一个变量进行读写操作,就可能会导致数据不一致的问题。原子性操作库通过使用原子操作来保证多个线程同时访问同一个变量时的正确性。

例,在多线程中进行加一减一操作,循环一定次数

#include <iostream>
#include <thread>
#include <atomic>
#include <time.h>
#include <mutex>
using namespace std;#define MAX 100000
#define THREAD_COUNT 20int total = 0;
mutex mt;void thread_task()
{for (int i = 0; i < MAX; i++){mt.lock();total += 1;total -= 1;mt.unlock();}
}int main()
{clock_t start = clock();thread t[THREAD_COUNT];for (int i = 0; i < THREAD_COUNT; ++i){t[i] = thread(thread_task);}for (int i = 0; i < THREAD_COUNT; ++i){t[i].join();}clock_t finish = clock();// 输出结果cout << "result:" << total << endl;cout << "duration:" << finish - start << "ms" << endl;system("pause");return 0;
}

从结果来看非常耗时,使用原子atomic,不需要使用mutex,(注意添加头文件)

#include <iostream>
#include <thread>
#include <atomic>
#include <time.h>
#include <mutex>
using namespace std;#define MAX 100000
#define THREAD_COUNT 20//原子操作  也不需要使用互斥锁
// atomic_int total(0);
atomic<int> total;void thread_task()
{for (int i = 0; i < MAX; i++){total += 1;total -= 1;}
}int main()
{clock_t start = clock();thread t[THREAD_COUNT];for (int i = 0; i < THREAD_COUNT; ++i){t[i] = thread(thread_task);}for (int i = 0; i < THREAD_COUNT; ++i){t[i].join();}clock_t finish = clock();// 输出结果cout << "result:" << total << endl;cout << "duration:" << finish - start << "ms" << endl;system("pause");return 0;
}

 关于具体函数的使用参考官方文档

4.条件变量condition_variable 

条件变量是c++11引入的一种同步机制,它可以阻塞一个线程或者多个线程,直到有线程通知或者超时才会唤醒正在阻塞的线程, 条件变量需要和锁配合使用,这里的锁就是上面的unique_lock。

其中有两个非常重要的接口,wait()和notify_one(),wait()可以让线程陷入休眠状态,notify_one()就是唤醒真正休眠状态的线程。还有notify_all()这个接口,就是唤醒所有正在等待的线程。

#include <iostream>
#include <deque>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;deque<int> q;
mutex mt;
condition_variable cond;void thread_producer()
{int count = 10;while (count > 0){unique_lock<mutex> unique(mt);q.push_front(count);unique.unlock();    // 解锁才能去唤醒cout << "producer a value: " << count << endl;cond.notify_one();   // 唤醒wait的线程会阻塞当前线程,this_thread::sleep_for(chrono::seconds(1));count--;}
}void thread_consumer()
{int data = 0;while (data != 1){unique_lock<mutex> unique(mt);while (q.empty())cond.wait(unique);      //解锁,线程被阻塞处于等待状态 |||| 被唤醒后优先获得互斥锁data = q.back();   // 使用 back 函数获取最后一个元素q.pop_back();cout << "consumer a value: " << data << endl;// 下面这行可以不写,unique_lock 离开作用域会调用析构判断是否解锁。// unique.unlock();     // 解锁后thread_producer获得互斥锁}
}int main()
{thread t1(thread_consumer);thread t2(thread_producer);t1.join();t2.join();system("pause");return 0;
}

thread_consumer 的执行流程如下

1.unique_lock<mutex> unique(mt); - 上锁互斥锁。

2.cond.wait(unique); - 释放互斥锁并等待通知。

3.当被通知后,重新获得互斥锁。

4.继续执行后面的代码,包括 data = q.back(); 和 q.pop_back();。

在上述过程中,unique_lock 会自动处理锁的上锁和解锁操作。当 cond.wait(unique); 执行时,它会将互斥锁解锁,并将线程置于等待状态。当线程被唤醒后,unique_lock 会自动重新对互斥锁上锁。

在 thread_producer 中,它获取互斥锁的步骤如下

1.unique_lock<mutex> unique(mt); - 上锁互斥锁。

2.q.push_front(count); - 操作共享资源。

3.unique.unlock(); - 解锁互斥锁。

4.cond.notify_one(); - 发送通知。

在这个过程中,unique_lock 会在 unique.unlock(); 处解锁互斥锁,以允许其他线程进入相应的临界区。当 cond.notify_one(); 发送通知后,thread_consumer 会被唤醒,并在 unique_lock 重新获取互斥锁后继续执行。

这两个互斥锁操作是同步的,不会引起冲突,因为它们是针对不同的互斥锁对象进行的。thread_consumer 上的互斥锁 mt 与 thread_producer 上的互斥锁 mt 是两个不同的互斥锁对象。

注意:在使用条件变量时,一般要在循环中等待条件,因为线程被唤醒后需要重新检查条件是否真的满足。 

更详细的参考文档

5.future 和 promise 

TODO

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/248411.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker由浅入深

一、什么是docker docker 顾名思义就是轮船的意思&#xff0c;轮船我们知道是通过集装箱运载货物的东西&#xff0c;那么docker其实也是类似的东西&#xff0c;只是装载的是虚拟的运行程序罢了。其中集装箱在docker里面被称为container&#xff08;后面以容器称之&#xff09;…

彻底解决 MAC Android Studio gradle async 时出现 “connect timed out“ 问题

最近在编译一个比较老的项目&#xff0c;git clone 之后使用 async 之后出现一下现象&#xff1a; 首先确定是我网络本身是没有问题的&#xff0c;尝试几次重新 async 之后还是出现问题&#xff0c;网上找了一些方法解决了本问题&#xff0c;以此来记录一下问题是如何解决的。 …

Python(19)Excel表格操作Ⅰ

目录 导包 读取EXCEL文件 1、获取worksheet名称 2、设定当前工作表 3、输出目标单元格数据 4、工作表.rows&#xff08;行&#xff09; 5、工作表.columns&#xff08;列&#xff09; 小结 导包 要想使用 python 操作 Excel 文件&#xff0c;应当导入 openpyxl 包。在…

上位机图像处理和嵌入式模块部署(视频处理vs图像处理)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 从目前发展的情况来看&#xff0c;视频处理会慢慢变成一种主流趋势。这里面的原因很多&#xff0c;比如说现在嵌入式soc的算力越来越强、获取图像的…

【环境配置】安装了pytorch但是报错torch.cuda.is_availabel()=Flase

解决思路&#xff1a;import torch正常&#xff0c;说明torch包安装正常&#xff0c;但是不能和gpu正常互动&#xff0c;猜测还是pytroch和cuda的配合问题 1.查看torch包所需的cuda版本 我的torch是2.0.1&#xff0c;在现在是比较新的包&#xff0c;需要12以上的cuda支持&…

鸿蒙首批原生应用!无感验证已完美适配鸿蒙系统

顶象无感验证已成功适配鸿蒙系统&#xff0c;成为首批鸿蒙原生应用&#xff0c;助力鸿蒙生态的快速发展。 作为全场景分布式操作系统&#xff0c;鸿蒙系统旨在打破不同设备之间的界限&#xff0c;实现极速发现、极速连接、硬件互助、资源共享。迄今生态设备数已突破8亿台&…

.net core 6 集成 elasticsearch 并 使用分词器

1、nuget包安装NEST、安装elasticsearch、kibana、ik分词器、拼音分词器 2、创建操作对象 //索引库 static string indexName "testparticper"; //es 操作对象 ElasticClient elasticClient new ElasticClient(new ConnectionSettings(new Uri("http://192.…

redis面试

0.思维导图 1. redis的数据类型♥♥♥ redis有五种数据类型&#xff0c;包括string,list,set,hash,zset;string就类似于java中的字符串&#xff0c;list就类似于Java中的列表&#xff0c;可以存放重复的元素&#xff0c;set就类似于java中的hashset&#xff0c;不能存放重复的…

索引文件的建立与查询示例(五)

隔了好几天没顾上更新内容&#xff0c;在此继续吧。还是得多动头脑&#xff0c;否则忙于日常事务&#xff0c;脑子不够灵活。在代码的世界里还是能有一些灵感。上次说到在内存中实现链表建立以及打印出链表的操作。这节我们试着将链表存入文件&#xff0c;并从文件中进行读取。…

关于maven项目构建的解释

在Idea中使用模块化构建项目 项目介绍&#xff1a; sky-server依赖sky-pojo和sky-common&#xff0c;继承sky-take-outsky-pojo继承sky-take-outsky-common继承sky-take-out 由于Idea编译器自动识别引入的模块&#xff0c;所以在Idea中可以运行项目。 在Idea中使用maven打包…

Java多线程--同步机制解决线程安全问题方式二:同步方法

文章目录 一、同步方法&#xff08;1&#xff09;同步方法--案例11、案例12、案例1之同步监视器 &#xff08;2&#xff09;同步方法--案例21、案例2之同步监视器的问题2、案例2的补充说明 二、代码及重要说明&#xff08;1&#xff09;代码&#xff08;2&#xff09;重要说明 …

用GPT写PHP框架

参考https://www.askchat.ai?r237422 写一个mvc框架 上面是简单的案例&#xff0c;完整的PHP框架&#xff0c;其核心通常包含以下几个关键组件&#xff1a; 1. 路由&#xff08;Routing&#xff09;&#xff1a;路由组件负责解析请求的URL&#xff0c;并将其映射到相应的控制…

Springboot项目启动后浏览器不能直接访问接口,而postman可以访问?

在云服务器上部署springboot后端时&#xff0c;项目启动后浏览器不能直接访问接口,而postman可以访问。这是当时困扰了我大半天的小问题&#xff0c;在我打开防火墙和阿里云安全组之后还是没解决。然后在网上搜了很多很多资料&#xff0c;以为是浏览器访问权限或者是https什么证…

【乳腺肿瘤诊断分类及预测】基于LVQNN学习向量量化神经网络

课题名称&#xff1a;基于LVQ神经网络的乳腺肿瘤诊断&#xff08;类型分类&#xff09; 版本日期&#xff1a;2023-03-10 运行方式: 直接运行0501_LVQ0501.m 文件即可 代码获取方式&#xff1a;私信博主或QQ&#xff1a;491052175 模型描述&#xff1a; 威斯康辛大学医学院…

H.264与H.265的主要差异

H.265仍然采用混合编解码&#xff0c;编解码结构域H.264基本一致&#xff0c; H.265与H.264的主要不同 编码块划分结构&#xff1a;采用CU (CodingUnit)、PU(PredictionUnit)和TU(TransformUnit)的递归结构。 并行工具&#xff1a;增加了Tile以及WPP等并行工具集以提高编码速…

优化器刺客之limit 1--Order by col limit n 代价预估优化探索

一、现象 order by 排序加了limit后更慢了&#xff1f; test# explain analyze select userid from dba_users where username like %aaaaaaaaaaaaaaaaaa% order by userid ;QUERY PLAN --------------…

第4章 python深度学习——(波斯美女)

第4章 机器学习基础 本章包括以下内容&#xff1a; 除分类和回归之外的机器学习形式 评估机器学习模型的规范流程 为深度学习准备数据 特征工程 解决过拟合 处理机器学习问题的通用工作流程 学完第 3 章的三个实例&#xff0c;你应该已经知道如何用神经网络解决分类问题和回归…

调试小结:PHY初始化前后Link Status是否能正确反应网线插上、拔下状态

1 说明 为了验证是否需要初始化PHY才能检测到网线插上、拔下&#xff0c;这里我们对比初始化PHY&#xff08;LAN8720&#xff09;前后&#xff0c;插拔网线PHY寄存器1的bit2的是否按照预期变化来进行测试。 我们查看的PHY寄存器是1的bit2&#xff0c;定义如下&#xff1a; 2…

anaconda离线安装包的方法

当设备没有网络时&#xff0c;可以使用有网络的设备先下载所需安装包&#xff0c;然后离线拷贝到需要安装的设备&#xff0c;最后安装。 一. 下载所需安装包 下载命令&#xff1a;使用pip download。详细描述参见pip download -h 以"blind-watermark"为例。 pip …

asp.net core监听本地ip地址

开发asp.net core的时候遇到一个问题我想提供访问供其他同事测试&#xff0c;但是默认都是localhost或者127.0.0.1。我想换成我的Ip地址访问但是不行&#xff0c;百度搜索需要更换监听的地址即修改launchSettings.json&#xff0c;修改为0.0.0.0:5248&#xff0c;这样不管local…