线程自动退出_C++基础 多线程笔记(一)

e08cf3f8ba5a84cf8806545fe0d5dcf6.png

join & detach

join和detach为最基本的用法,join可以使主线程(main函数)等待子线程(自定义的function_1函数)完成后再退出程序,而detach可以使子线程与主线程毫无关联的独立运行,当主线程执行完毕后直接退出程序,不管子线程是否执行完毕。

#include<iostream>
#include<thread>
using namespace std;// 子线程函数
void function_1()
{for(int i=10;i>0;i--) // 循环10次输出cout << "=============Hello=============" << endl;
}int main()
{thread t1(function_1);//线程开始//t1.join();//方式1:结合(等待其完成)t1.detach();//方式2:分离(使其自行运行)(cout未来得及输出完毕,主线程已结束)cout << "~~~~~~~~~~~World~~~~~~~~~~~" << endl;if (t1.joinable()){t1.join();}return 0;
}

detach方法的执行结果如下,可以看出子线程没来得及执行完毕。

=============Hello=============
~~~~~~~~~~~World~~~~~~~~~~~
=请按任意键继续. . .

如果换成join方法,则可以输出10条Hello语句。

=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
=============Hello=============
~~~~~~~~~~~World~~~~~~~~~~~
请按任意键继续. . .

try-catch异常捕获机制的使用

join可以使某些比较重要的函数执行完毕后再退出,但当程序出现异常时,程序仍会直接退出,join没有起到应有的作用,这是可以通过try-catch异常捕获机制,结合join方法,使某些函数(子线程)在程序出现异常时也能先执行完毕再退出,例子如下,通过OpenCV读取显示一张不存在的图片产生异常。

#include<iostream>
#include<thread>
#include<opencv2/opencv.hpp>// 子线程函数(假定该函数比较重要,无论如何都要执行完毕再退出程序)
void function_1()
{for (int i = 0; i < 100; i++){std::cout << "========Hello=======" << i << std::endl;}
}int main()
{std::thread t1(function_1);//t1线程开始运行try //【捕获异常的范围】{cv::Mat img = cv::imread("1.jpg");//读取一张不存在的图片,使下句的图片显示出现异常cv::imshow("===", img);//此处将出现异常!?错误?//出现异常会导致整个程序直接退出//捕获异常后,可以进行补救,如使t1子线程执行完毕。}catch (...)//捕获所有异常{std::cout << "catch..............." << std::endl;t1.join();//使子线程执行完毕throw;}t1.join();std::cout << "主程序正常退出" << std::endl;return 0;
}

可以看出运行后产生了一个OpenCV Error,没能输出"主程序正常退出" ,但子线程在程序出现异常后依然可以继续执行完毕。

========Hello=======OpenCV Error: Assertion failed (size.width>0 && size.height>0) in cv::imshow, file D:Tools1opencvopencvsourcesmoduleshighguisrcwindow.cpp, line 325
0
========Hello=======1catch...............========Hello=======2
========Hello=======3
========Hello=======4
========Hello=======5
此处省略...
========Hello=======98
========Hello=======99

通过类构造子线程 & ref方法传参

C++开发中更常使用类作为子线程函数而不是单独的某个函数。

注意一点在线程按引用传递参数时的写法,需要使用std::ref方法。

#include<iostream>
#include<thread>
#include<string>class Fctor
{
public:void operator()(std::string& msg)//按【引用】传递参数{std::cout << "from t1:" << msg << std::endl; msg = "++++++++Hello+++++++++";//修改传入的参数(用于后面的主线程输出)}
};int main()
{std::string s = "-----------World-----------";//待传入的参数(用于子线程输出)// 方式1a:这种方式会自动复制一份参数传进去//Fctor fct;//std::thread t1(fct,s);//t1线程开始运行// 方式1b:这种方式会自动复制一份参数传进去//std::thread t1((Fctor()), s);//t1线程开始运行// 方式2a:按引用传递Fctor fct;std::thread t1(fct, std::ref(s));//t1线程开始运行// 方式2b:按引用传递//std::thread t1((Fctor()), std::ref(s));t1.join();std::cout << "from main:" << s << std::endl;return 0;
}

运行结果,方式1a或1b:

from t1:-----------World-----------
from main:-----------World-----------
请按任意键继续. . .

方式2a或2b:

from t1:-----------World-----------
from main:++++++++Hello+++++++++
请按任意键继续. . .

mov方法传参 & 线程对象移动

除了使用ref方法对子线程进行传参,还可以使用mov方法传参,此外mov还可以移动线程对象。

#include<iostream>
#include<thread>
#include<string>class Fctor
{
public:void operator()(std::string& msg)//按引用传递参数{std::cout << "from t1:" << msg << std::endl;msg = "++++++++++++Hello++++++++++";}
};int main()
{std::string s = "----------------World---------------";std::cout << "Main_thread_ID:" << std::this_thread::get_id() << std::endl;//主线程IDstd::thread t1((Fctor()), std::move(s));//子线程1(将字符串从主线程移动到子线程)std::cout << "Sub_thread1_ID" << t1.get_id() << std::endl;//线程对象只能被移动,不能被复制。std::thread t2 = std::move(t1);//子线程2(接管子线程1,此时子线程1为空?!)std::cout << "Sub_thread2_ID" << t2.get_id() << std::endl;//可以看到两个子线程的ID是相同的!t2.join();//等待子线程2结束//检测硬件并发特性(此句只是用来显示计算机支持的并发线程数量)std::cout << std::thread::hardware_concurrency() << std::endl;return 0;
}

运行结果如下,可以看出传参无误,并且两个子线程的ID相同,说明子线程对象移动成功。

Main_thread_ID:36576
from t1:Sub_thread1_ID37472----------------World---------------Sub_thread2_ID37472
8
请按任意键继续. . .

mutex & lock_guard

mutex即互斥量,可理解为一把锁,访问某些资源时先加锁,访问后解锁。 另一进程访问同一资源时,首先尝试加锁,如果锁处于未释放状态则无法加锁,需等待其它线程对锁的释放。

#include<iostream>
#include<thread>
#include<string>
#include<mutex>std::mutex mu;//【互斥对象】=》一把锁通过函数调用cout,并为cout加锁,防止同时访问cout
void share_print(std::string msg, int id)
{mu.lock();std::cout << msg << id << std::endl;mu.unlock();
}//子线程函数
void function_1()
{for(int i = 0; i < 100; i++)share_print("==========from t1:" ,i );
}int main()//主线程
{std::thread t1(function_1);//t1线程开始运行for (int i = 0; i < 100; i++){share_print("+++++++++++++++++from main:", i);}t1.join();//等待子线程结束return 0;
}

运行结果类似如下:

==========from t1:0
+++++++++++++++++from main:0
==========from t1:1
+++++++++++++++++from main:1
==========from t1:2
==========from t1:3
==========from t1:4
==========from t1:5
省略...

如果未使用加锁机制,两线程会互相争抢cout的使用权,从而导致输出混乱,注释掉mu.lock()与mu.unlock()后的输出结果如下:

==========from t1:0+++++++++++++++++from main:0==========from t1:1+++++++++++++++++from main:1==========from t1:2+++++++++++++++++from main:2==========from t1:3
+++++++++++++++++from main:3==========from t1:4==========from t1:5+++++++++++++++++from main:4
省略...

由于lock()与unlock()必须成对出现,为方便管理,出现了lock_guard,它可以对mutex进行管理,自动实现lock()与unlock(),原理是在其构造与析构中自动调用。另外,还可有附加参数。

修改上面的share_print为如下,可实现同样的效果。

void share_print(std::string msg, int id)
{std::lock_guard<std::mutex> guard(mu);std::cout << msg << id << std::endl;
}

下面的代码是将share_print封装到一个类中,并添加将输出信息同时保存到文件中的功能:

#include<iostream>
#include<thread>
#include<string>
#include<mutex>
#include<fstream>class LofFile
{
public:LofFile(){ f.open("log.txt"); }~LofFile(){ f.close(); }void shared_print(std::string id, int value){std::lock_guard<std::mutex> locker(m_mutex);f << "from " << id << ":" << value << std::endl;//写入文件std::cout << "from " << id << ":" << value << std::endl;//输出}private://受保护的成员std::mutex m_mutex;//锁std::ofstream f;//此时f完全在锁的保护下
};void function_1(LofFile& log)
{for (int i = 0; i > -100; i--)log.shared_print("t1", i);
}int main()//主线程
{LofFile log;std::thread t1(function_1,std::ref(log));//t1线程开始运行for (int i = 0; i < 100; i++){log.shared_print("main", i);}t1.join();return 0;
}

死锁 & adopt_lock

当某个资源被两把以上的锁嵌套加锁,且锁的顺序不一致时,可能发生死锁。

原因在于多个线程可能各自加了1把锁后,同时在等待对方释放剩余的锁。

最简单的解决方法是:只要锁的顺序一致,就不会死锁。

#include<iostream>
#include<thread>
#include<string>
#include<mutex>
#include<fstream>class LogFile
{std::mutex m_mutex;//锁1std::mutex m_mutex2;//锁2std::ofstream f;
public:LogFile()//构造函数,初始化时新建一个txt文件{f.open("log.txt");}void shared_print(std::string id, int value){std::lock_guard<std::mutex> locker(m_mutex);//锁住m_mutex成员std::lock_guard<std::mutex> locker2(m_mutex2);std::cout << id << ":" << value << std::endl;}void shared_print2(std::string id, int value){std::lock_guard<std::mutex> locker2(m_mutex2);//【出现死所,交换和下一行的位置即可】std::lock_guard<std::mutex> locker(m_mutex);//std::lock_guard<std::mutex> locker2(m_mutex2);std::cout << id << ":" << value << std::endl;}
};void function_1(LogFile& log)
{for (int i = 0; i > -1000; i--)log.shared_print(std::string("from t1:"), i);
}int main()//主线程
{LogFile log;std::thread t1(function_1, std::ref(log));//t1线程开始运行for (int i = 0; i < 1000; i++){log.shared_print2(std::string("from main:"), i);}t1.join();return 0;
}

某次运行结果如下,程序运行到某时刻卡住了:

from main::0
from main::1
省略...
from main::154
from main::155
from main::156
from main::157
from t1::0

当程序比较复杂时,手动方法管理加锁顺序可能相当麻烦,这是就出现了adopt_lock参数来解决。

lock+lock_guard的adopt_lock参数自动避免死锁问题。

lock()可同时管理多个锁,顺序无影响,同时锁住多个锁,若不可,先释放,然后继续尝试。 lock_guard()的adopt_lock参数即抛弃lock操作,因为前面(必须)已加锁,只使用其自动unlock功能。

#include<iostream>
#include<thread>
#include<string>
#include<mutex>
#include<fstream>class LogFile
{std::mutex m_mutex;//锁1std::mutex m_mutex2;//锁2std::ofstream f;
public:LogFile(){f.open("log.txt");}void shared_print(std::string id, int value){std::lock(m_mutex, m_mutex2);//lock()同时管理多个锁std::lock_guard<std::mutex> locker(m_mutex,std::adopt_lock);//adopt_lock即抛弃lock操作,因为上句已加锁std::lock_guard<std::mutex> locker2(m_mutex2, std::adopt_lock);//在析构时自动unlock()std::cout << id << ":" << value << std::endl;}void shared_print2(std::string id, int value){std::lock(m_mutex, m_mutex2);std::lock_guard<std::mutex> locker2(m_mutex2, std::adopt_lock);std::lock_guard<std::mutex> locker(m_mutex, std::adopt_lock);std::cout << id << ":" << value << std::endl;}
};void function_1(LogFile& log)
{for (int i = 0; i > -1000; i--)log.shared_print(std::string("from t1:"), i);
}int main()//主线程
{LogFile log;std::thread t1(function_1, std::ref(log));//t1线程开始运行for (int i = 0; i < 1000; i++){log.shared_print2(std::string("from main:"), i);}t1.join();return 0;
}

运行结果如下,不会出现死锁:

from t1::0
from main::0
from t1::-1
from main::1
省略...
from t1::-997
from main::994
from t1::-998
from main::995
from t1::-999
from main::996
from main::997
from main::998
from main::999
请按任意键继续. . .

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

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

相关文章

数组拼接时中间怎么加入空格_【题解二维数组】1123:图像相似度

1123&#xff1a;图像相似度时间限制: 1000 ms 内存限制: 65536 KB【题目描述】给出两幅相同大小的黑白图像(用0-1矩阵)表示&#xff0c;求它们的相似度。说明&#xff1a;若两幅图像在相同位置上的像素点颜色相同&#xff0c;则称它们在该位置具有相同的像素点。两幅图像的…

(旧)子数涵数·C语言——条件语句

首先&#xff0c;我们讲一下理论知识&#xff0c;在编程中有三种结构&#xff0c;分别是顺序结构、条件结构、循环结构&#xff0c;如果用流程图来表示的话就是&#xff1a; 那么在C语言中&#xff0c;如何灵活运用这三种结构呢&#xff1f;这就需要用到控制语句了。 而条件语句…

识别操作系统

使用p0f进行操作系统探测 p0f是一款被动探测工具&#xff0c;通过分析网络数据包来判断操作系统类型。目前最新版本为3.06b。同时p0f在网络分析方面功能强大&#xff0c;可以用它来分析NAT、负载均衡、应用代理等。 p0f的命令参数很简单&#xff0c;基本说明如下&#xff1a; l…

常用RGB颜色表

转载于:https://www.cnblogs.com/Itwonderful/p/5550800.html

linux ssh yum升级_Linux 运维必备的 13 款实用工具,拿好了

作者丨Erstickthttp://blog.51cto.com/13740508/2114819本文介绍几款 Linux 运维比较实用的工具&#xff0c;希望对 Linux 运维人员有所帮助。1. 查看进程占用带宽情况 - NethogsNethogs 是一个终端下的网络流量监控工具可以直观的显示每个进程占用的带宽。下载&#xff1a;htt…

iOS应用如何支持IPV6

本文转自 http://www.code4app.com/forum.php?modviewthread&tid8427&highlightipv6 果然是苹果打个哈欠&#xff0c;iOS行业内就得起一次风暴呀。自从5月初Apple明文规定所有开发者在6月1号以后提交新版本需要支持IPV6-Only的网络&#xff0c;大家便开始热火朝天的研…

SQL Server -- SQLserver 存储过程执行错误记录到表

SQLserver 存储过程执行错误记录到表 From: http://blog.csdn.net/leshami/article/details/51333650 对于在执行存储过程中碰到的一些错误&#xff0c;如果未及时捕获或者说传递给前端应用程序来&#xff0c;在这样的情形下&#xff0c;故障的排查显得尤为困难。基于此&…

Windows下C语言连接Oracle数据库

为什么80%的码农都做不了架构师&#xff1f;>>> 最近公司有个项目需要用到Oracle数据库&#xff0c;我负责前期的调研。由于项目要用到C和PHP两种语言&#xff0c;所以先收集这两种语言连接Oracle的方法。PHP使用的是Laravel框架&#xff0c;直接使用了Laravel-OCI…

SU suspecfk命令学习

用suplane生成平面&#xff0c;并查看其FK谱&#xff0c; 水平反射界面经FK变换后&#xff0c;波数为0&#xff0c; 正好处于临界&#xff0c;乃奎斯特频率&#xff0c; 有空间假频&#xff0c; Over&#xff0c;不足之处&#xff0c;欢迎批评指正。 转载于:https://www.cnblog…

dblink查询_分库数据如何查询统计

分库后的计算不能直接使用SQL&#xff1b;异构库 SQL 函数不尽相同&#xff1b;JAVA 硬编码实施难度大&#xff1b;即使借助透明网关访问远程数据库&#xff0c;分库性能优化也是头疼问题。一般常规办法&#xff1a;方法1&#xff1a;java硬编码简单的跨库count运算&#xff0c…

【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸

类的继承案例解析&#xff0c;python相关知识延伸 作者&#xff1a;白宁超 2016年10月10日22:36:57 摘要&#xff1a;继<快速上手学python>一文之后&#xff0c;笔者又将python官方文档认真学习下。官方给出的pythondoc入门资料包含了基本要点。本文是对文档常用核心要点…

领域驱动设计:软件核心复杂性应对之道_人人都可以领域驱动设计(一)

最近几年&#xff0c;领域驱动设计&#xff08;Domain-Driven Design&#xff0c;DDD&#xff09;这个术语越来越多地出现在软件工程师的视野里。对DDD不熟悉的人可能会觉得它是软件领域里的一个新的概念&#xff0c;但是实际上&#xff0c;Eric Evans在十几年前就已经提出了这…

linux 进程通信 消息队列

详解linux进程间通信-消息队列 前言&#xff1a;前面讨论了信号、管道的进程间通信方式&#xff0c;接下来将讨论消息队列。 一、系统V IPC 三种系统V IPC&#xff1a;消息队列、信号量以及共享内存&#xff08;共享存储器&#xff09;之间有很多相似之处。 每个内核中的 I P …

wx.checkjsapi是写在config里面吗_用Python写一个程序,解密游戏内抽奖的秘密

前言本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理。作者&#xff1a; 极客挖掘机PS&#xff1a;如有需要Python学习资料的小伙伴可以加点击下方链接自行获取http://t.cn/A6Zvjdun分析需求我们先整理下思…

Dev C++安装第三方库boost

Dev_C安装第三方库boost 安装步骤 准备工作下载boost库&#xff0c;下载地址https://sourceforge.net/projects/boost/1. 设置GCC的环境变量PATH 设置环境变量path,在其中加上DEV-C编译器的路径&#xff08;gcc.exe所在路径&#xff09;&#xff0c;如C:\Program Files (x86)…

bash的一些小技巧

1、从输入读入变量 eg:read -ep "input yes or no: " flag 用e选项表示编辑&#xff0c;可以使用backspace删除 2、数组 a、索引数组 declare -a arr(var1 var2 var3) 用空格分割&#xff0c;如果直接访问变量$arr&#xff0c; 则获取的是数组的第一个元素&#xff0…

golang switch_为什么程序员都不喜欢使用 switch ,而是大量的 if……else if ?

点击上方“我要学编程”&#xff0c;选择“置顶/星标公众号”福利干货&#xff0c;第一时间送达&#xff01;来自 | C语言Plus请用5秒钟的时间查看下面的代码是否存在bug。OK&#xff0c;熟练的程序猿应该已经发现Bug所在了&#xff0c;在第13行下面我没有添加关键字break; 这就…

RabbitMQ 安装与简单使用

在企业应用系统领域&#xff0c;会面对不同系统之间的通信、集成与整合&#xff0c;尤其当面临异构系统时&#xff0c;这种分布式的调用与通信变得越发重要。其次&#xff0c;系统中一般会有很多对实时性要求不高的但是执行起来比较较耗时的地方&#xff0c;比如发送短信&#…

windows svn

windows svn 1.1Svn和VisualSvn介绍 VisualSvn Server2.5.6&#xff08;版本控制服务器&#xff09;免费开源软件 是基于Windows平台上的Subversion服务器&#xff0c;它是免费的 官方下载&#xff1a; http://www.visualsvn.com/files/VisualSVN-Server-2.5.6.msi TortoiseSvn…

docker-compose下载慢_编写Docker Compose时要注意的五大常见错误

在构建容器化的应用时&#xff0c;开发人员往往需要某种方法来引导启动目标容器&#xff0c;以对其进行代码级别的测试。尽管业界有许多方法可以实现该目的&#xff0c;但Docker Compose是目前最受欢迎的一种方法。它能够让如下两个方面变得容易实现&#xff1a;指定在开发过程…