C++11 Thread线程库的使用

C++11 Thread线程库的使用

传统的C++(C++11标准之前)中并没有引入线程这个概念,在C++11出来之前,如果我们想要在C++中实现多线程,需要借助操作系统平台提供的API,比如Linux的,或者windows下的 。
本文详细介绍C++11 线程库的基本使用,包括如何创建线程、启动线程、等待线程完成、如何分离线程。

多线程理解视频动画

文章目录

  • C++11 Thread线程库的使用
  • 1.线程的概念及使用
    • 1.1 如果是在单线程下,启动线程后,主程序不会等待子线程执行完函数。想让主程序等待子线程执行完毕。这里使用join()函数
    • 1.2 给线程函数传递参数
    • 1.3 分离线程detach(),主线程执行完毕,子线程在后台持续运行
    • 1.4 判断线程是否可以调用join()或者detach()
    • 1.5 判断join()其实是个阻塞函数
  • 2.线程函数中的数据未定义错误
    • 2.1传递临时变量的问题
      • 2.1.1 传引用
      • 2.1.2 传指针
    • 2.2 传递指针或引用指向局部变量的问题:
    • 2.3 传递指针或引用指向已释放的内存的问题
    • 2.4 类成员函数作为入口函数,类对象被提前释放
    • 2.5 入口函数为类的私有成员函数

1.线程的概念及使用

线程:进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。
进程:进程就是运行中的程序

  • 线程内核对象。操作系统用它来管理线程,存放线程统计信息。
  • 线程堆栈,用于维护线程在执行代码时,需要的所有函数参数和局部变量。
  • 线程的最大数量取决于于CPU的核心数

线程安全:不论运行多少次,如果多线程程序每一次运行的结果都跟单线程运行的结果是始终如一的,那么表名你的线程是安全的。

线程函数:默认情况下我们所写的代码都是只有一个线程的,而这个线程的入口函数是main() 函数, 这是系统默认的。而我们创建的另一个线程也需要一个函数来进入, 这个函数叫做线程函数。

在这里插入图片描述
示例1:


//多线程好处//任务分解:耗时的操作,任务分解,实时响应
//数据分解:充分利用多核CPU处理数据
//数据流分解:读写分离,解耦合设计//linux lpthread//要创建线程,我们需要一个可调用的函数或函数对象,作为线程的入口点。
//在C++11中我们可以使用函数指针、函数对象或lambda表达式来实现。创建线程的基本语法如下!//C++11 Thread线程库的基本使用
//创建线程 启动线程 等待线程 完成线程分离
//要创建线程,我们需要一个可调用的函数或函数对象,作为线程的入口点#include<iostream>
#include<thread>
void printHelloWorld()
{std::cout << "Hello World!" << std::endl;}int main()
{//1.创建线程std::thread thread1(printHelloWorld);return 0;
}

如果是在单线程下,启动线程后,主程序不会等待子线程执行完函数。所以会报错。
在这里插入图片描述

1.1 如果是在单线程下,启动线程后,主程序不会等待子线程执行完函数。想让主程序等待子线程执行完毕。这里使用join()函数

#include<iostream>
#include <thread>
void printHelloWorld()
{std::cout << "Hello World!" << std::endl;}int main()
{//1.创建线程std::thread thread1(printHelloWorld);thread1.join();return 0;
}

1.2 给线程函数传递参数

#include<iostream>
#include <thread>
#include<string>
void printHelloWorld(std::string msg)
{//std::cout << "Hello World!" << std::endl;std::cout << msg << std::endl;
}int main()
{//1.创建线程std::thread thread1(printHelloWorld, "Hello World!");thread1.join();return 0;
}

1.3 分离线程detach(),主线程执行完毕,子线程在后台持续运行

#include <iostream>
#include <thread>
#include <string>
void printHelloWorld(std::string msg)
{//std::cout << "Hello World!" << std::endl;std::cout << msg << std::endl;
}int main()
{//1.创建线程std::thread thread1(printHelloWorld, "Hello World!");//thread1.join();//等待线程执行完毕后再继续往下执行thread1.detach();//分离线程,主线程执行完毕,子线程在后台持续运行return 0;
}

在这里插入图片描述

1.4 判断线程是否可以调用join()或者detach()

#include <iostream>
#include <thread>
#include <string>
void printHelloWorld(std::string msg)
{//std::cout << "Hello World!" << std::endl;std::cout << msg << std::endl;
}int main()
{//1.创建线程std::thread thread1(printHelloWorld, "Hello World!");//thread1.join();//等待线程执行完毕后再继续往下执行//thread1.detach();//分离线程,主线程执行完毕,子线程在后台持续运行bool idJoin = thread1.joinable();if (idJoin){thread1.join();//等待线程执行完毕后再继续往下执行}return 0;
}

如果说我们对一个不能够调用join()或者detach()的线程进行强行调用,程序报错systerm_error

1.5 判断join()其实是个阻塞函数

#include <iostream>
#include <thread>
#include <string>
void printHelloWorld(std::string msg)
{//std::cout << "Hello World!" << std::endl;//std::cout << msg << std::endl;for (int i = 0; i < 10000; i++)std::cout << i << std::endl;
}int main()
{//1.创建线程std::thread thread1(printHelloWorld, "Hello World!");//thread1.join();//等待线程执行完毕后再继续往下执行//thread1.detach();//分离线程,主线程执行完毕,子线程在后台持续运行bool idJoin = thread1.joinable();if (idJoin){thread1.join();//等待线程执行完毕后再继续往下执行}std::cout << "over" << std::endl;return 0;
}

在这里插入图片描述

2.线程函数中的数据未定义错误

2.1传递临时变量的问题

2.1.1 传引用

#include<iostream>
#include<thread>void foo(int &x)
{x = x + 1;}
int main()
{std::thread  t(foo, 1);//传递临时变量t.join();return 0;}

在这里插入图片描述

修改为

#include<iostream>
#include<thread>void foo(int &x)
{x = x + 1;}
int main()
{int a = 1;std::thread  t(foo, std::ref(a));//传递临时变量t.join();std::cout << "a=" << a << std::endl;return 0;}

在这里插入图片描述

2.1.2 传指针

#include<iostream>
#include<thread>void foo(int *x)
{*x = *x + 100;}
int main()
{int a = 1;std::thread  t(foo, &a);//传递临时变量t.join();std::cout << "a=" << a << std::endl;return 0;}

在这里插入图片描述

2.2 传递指针或引用指向局部变量的问题:

#include<iostream>
#include<thread>
std::thread t;
int a = 1;
void foo(int *x)
{*x = *x + 1;std::cout << "*x=" << *x << std::endl;
}
void test()
{//int a = 1;t = std::thread(foo, &a);}int main()
{test();t.join();//std::cout << "a=" <<a << std::endl;std::cout << "over" << std::endl;return 0;}

2.3 传递指针或引用指向已释放的内存的问题

#include <iostream>
#include <thread>
void foo(int& x) 
{std::cout << x << std::endl; // 访问已经被释放的内存
}
int main() 
{int* ptr = new int(1);std::thread t(foo, *ptr); // 传递已经释放的内存delete ptr;t.join();return 0;
}

在这里插入图片描述

2.4 类成员函数作为入口函数,类对象被提前释放

#include <iostream>
#include <thread>class MyClass 
{
public:void func() {std::cout << "Thread " << std::this_thread::get_id()<< " started" << std::endl;// do some workstd::cout << "Thread " << std::this_thread::get_id()<< " finished" << std::endl;}
};int main() 
{MyClass obj;std::thread t(&MyClass::func, &obj);// obj 被提前销毁了,会导致未定义的行为return 0;
}

在这里插入图片描述
上面的代码中,在创建线程之后,obj 对象立即被销毁了,这会导致在线程执行时无法访问 obj 对象,可能会导致程序崩溃或者产生未定义的行为。

为了避免这个问题,可以使用 std::shared_ptr 来管理类对象的生命周期,确保在线程执行期间对象不会被销毁。具体来说,可以在创建线程之前,将类对象的指针封装在一个 std::shared_ptr 对象中,并将其作为参数传递给线程。这样,在线程执行期间,即使类对象的所有者释放了其所有权,std::shared_ptr 仍然会保持对象的生命周期,直到线程结束。

以下是使用 std::shared_ptr 修复上面错误的示例:

#include <iostream>
#include <thread>
#include<memory>
class MyClass 
{
public:void func() {std::cout << "Thread " << std::this_thread::get_id()<< " started" << std::endl;// do some workstd::cout << "Thread " << std::this_thread::get_id()<< " finished" << std::endl;}
};int main() 
{std::shared_ptr<MyClass> obj = std::make_shared<MyClass>();std::thread t(&MyClass::func, obj);t.join();// obj 被提前销毁了,会导致未定义的行为return 0;
}

在这里插入图片描述

2.5 入口函数为类的私有成员函数

//5.入口函数为类的私有成员函数#include <iostream>
#include <thread>class MyClass 
{
private:friend void myThreadFunc(MyClass* obj);void privateFunc() {std::cout << "Thread "<< std::this_thread::get_id() << " privateFunc" << std::endl;}
};void myThreadFunc(MyClass* obj) 
{obj->privateFunc();
}int main() {MyClass obj;std::thread thread_1(myThreadFunc, &obj);thread_1.join();return 0;
}

在这里插入图片描述

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

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

相关文章

PPT课件培训视频生成系统实现全自动化

前言 困扰全动自化的重要环节&#xff0c;AI语音合成功能&#xff0c;终于可以实现自动化流程&#xff0c;在此要感谢团队不懈的努力和韧性的精神&#xff01; 实现原理 请参照我的文章《Craneoffice云PPT课件培训视频生成系统》 基本流程 演示视频 PPT全自动 总结 过去实…

常见弯道输送机有哪些

提到弯道输送机您可能首先想到的就是弯道滚筒线&#xff0c;其实除了滚筒线之外&#xff0c;也有一些其他线体可以做弯道&#xff0c;下面就为您总结了4种常见的弯道输送机。 1、弯道皮带线&#xff1a;即线体转弯处设计成皮带输送机&#xff0c;这种形式的转弯设计可以实现不同…

如何在 Spring Boot 中进行文件上传

在 Spring Boot 中进行文件上传 文件上传是Web应用程序中常见的功能之一&#xff0c;它允许用户将文件从客户端上传到服务器。Spring Boot提供了便捷的方式来处理文件上传&#xff0c;并且整合了Spring框架的强大功能&#xff0c;使文件上传变得相对简单。本文将介绍如何在Spr…

【PPT制作】基础篇

文章目录 一、PPT制作必要的基础设置1.1 自动保存1.2 字体嵌入1.3 撤销步数1.4 图像大小和质量 二、必备快捷键三、设计四原则四、总结 ヾ(๑╹◡╹)&#xff89;" 没有坚持的努力&#xff0c;本质上并没有多大意义ヾ(๑╹◡╹)&#xff89;" 一、PPT制作必要的基础…

面对研究生粉丝机器视觉择业问题-视觉人机器视觉寄语

机器视觉是有门槛&#xff0c;他是一门综合学科。对基础课程的知识点有一定的需求&#xff0c;对于后来者​肯定没有一定储备&#xff0c;肯定要要进一步加深学习的。 人不吃饭&#xff0c;真的会饿死的。无论谁想学习机器视觉&#xff0c;我第一个劝导的是尽快就业。工作经验过…

HTTPS 加密工作过程

引言 HTTP 协议内容都是按照文本的方式明文传输的&#xff0c;这就导致在传输过程中出现一些被篡改的情况。例如臭名昭著的运营商劫持。显然&#xff0c; 明文传输是比较危险的事情&#xff0c;为此引入 HTTPS &#xff0c;HTTPS 就是在 HTTP 的基础上进行了加密, 进一步的来保…

SaaS是什么?

可做一个比喻&#xff0c;不仅把SaaS是什么讲的很清楚&#xff0c;还把Iaas、PaaS和SaaS之间的区别弄清楚了。 这个比喻就是“如果把云计算简单想象成一台大电脑&#xff0c;那么IaaS是直接给你一台裸机&#xff0c;PaaS是安装好操作系统和基础运行环境再给你&#xff0c;而Sa…

泛函的含义,泛函分析

经常有同事和朋友讨论泛函分析是做什么的&#xff0c;所以做个小log 1. 泛函的含义 泛函的含义&#xff0c;笼统说&#xff0c;泛函是符合某种性质的任意函数&#xff1b;因为是任意的&#xff0c;所以就是泛泛的&#xff1b;但也没有不着边际的泛。 2. 泛函的例子 2.1 符合半…

区块链的两个核心概念之一签名, 另一个是共识.

Alice的公私钥&#xff0c; 签名和验证签名仅仅确定了Alice对数字资产A所有权的宣言. 之后, Bob也可以用自己的私钥对资产A进行签名宣誓所有权。区块链中叫双花&#xff0c;即重复宣称所有权&#xff0c; 也称重复花费交易。这时候需要共识算法(集体成员pow或委员会代表pos监督…

移远通信EM060K系列LTE-A Cat 6模组完成全球认证覆盖

近日&#xff0c;移远通信LTE-A Cat 6模组EM060K系列顺利完成全球认证覆盖&#xff0c;将以卓越的性能和品质助力海内外客户终端大规模部署&#xff0c;为其提供畅快的高速网络连接。同时&#xff0c;凭借着有竞争力的性能和成本优势&#xff0c;EM060K系列将加速释放海外固定无…

matlab 图像均值滤波

目录 一、算法原理二、代码实现三、结果展示本文由CSDN点云侠翻译,放入付费专栏只为防不要脸的爬虫。专栏值钱的不是本文,切勿因本文而订阅。 一、算法原理 均值滤波是一种常用的线性滤波方法,用于平滑图像并减少噪声。它的实现过程如下: 确定滤波器的大小:选择一个固定的…

科技资讯|苹果下一代Vision Pro头显将更小更轻,预装处方镜片

据彭博社的 Mark Gurman 在《Power On》新闻简报中透露&#xff0c;苹果和 Meta 的混合现实头显还未发售&#xff0c;但两家的下一代机型的开发工作已经在顺利进行。 据报道&#xff0c;苹果下代产品的一个重点是通过更小、更轻的设计&#xff0c;使其设备佩戴起来更加舒适。据…

利用正则表达式进行数据采集和处理

目录 一、正则表达式的概述 二、正则表达式在数据采集中的运用 1、匹配和提取数据 2、数据清洗 3、数据验证 三、Python中的re模块介绍 1、re.match()方法 2、re.search()方法 总结 正则表达式是一种强大的文本处理工具&#xff0c;它可以用于模式匹配、提取、替换等操…

Python中套接字实现服务端和客户端3-3

3 创建客户端的步骤 创建客户端的步骤如图5所示。 图5 创建客户端的步骤 从图5可以看出&#xff0c;对于客户端来说&#xff0c;首先创建套接字&#xff0c;之后通过创建的套接字去连接服务端&#xff0c;如果连接成功&#xff0c;则继续通过该套接字向服务端发送数据&#x…

请求的转发和重定向

RequestDispatcher接口实现转发&#xff1a; jsp1上链接到Servlet&#xff0c;Servlet再转发&#xff08;关键在这里怎么实现转发&#xff1f;&#xff1f;&#xff09; 演示index.html页面---->Servlet1(转发到)------>Servlet2 实现转发流程 1.用HttpServletReques…

【LeetCode刷题笔记】哈希查找

771. 宝石与石头 解题思路&#xff1a; 1. HashSet &#xff0c;把所有 宝石 加入 set , 然后遍历检查 每一块石头是否包含在set中 &#xff0c;若包含就是宝石。 2. 计数数组map, 把所有 宝石 进行 count 数组 计数 &#xff0c;, 然后遍历检查 每一块石头是否 count[stone] …

如何在Firefox中配置HTTP?

在浏览器中配置HTTP是一个常见的需求&#xff0c;它可以让我们轻松访问需要的网站或保护个人隐私。本文将为您详细介绍如何在Firefox浏览器中配置HTTP应用&#xff0c;帮助您实现无缝的HTTP体验。无论您是初次接触HTTP还是有一定经验的用户&#xff0c;本文都能为您提供实用的操…

Docker-consul容器服务更新与发现

目录 一、consul简介 1、什么是服务注册与发现 2、什么是consul 3、consul的关键特性 二、consul部署 1、consul服务器部署 1.1 建立consul服务 2、查看集群信息 3、通过http api 获取集群信息 三、registrator部署 1、安装Gliderlabs/Registrator 2、测试服务发现…

浅谈高速公路服务区分布式光伏并网发电

前言 今年的国家经济工作会议提出&#xff1a;将“做好碳达峰、碳中和工作”作为 2021年的主要任务之一&#xff0c;而我国高速公路里程 15.5万公里&#xff0c;对能源的需求与日俱增&#xff0c;碳排放量增速明显。 为了实现采用减少碳排放量&#xff0c;采用清洁能源替代的…

软件培训测试高级工程师多测师肖sir__html之作业11

html之作业 案例1&#xff1a; 截图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>表单</title></head><body><table style"background-color:red" bo…