C++11 Thread线程库的使用

news2024/11/26 11:47:15

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 work
		std::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 work
		std::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.coloradmin.cn/o/1070691.html

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

相关文章

【数据结构】什么是算法

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.算法的定义 1.算法的概念 2.数据结构与算法的关系 二.算法的特性 输入 输出 有穷性 确定性 可行性 三.算法的设计要求 1.正确性 2.可读性 3.健壮性 4.效…

DALL·E 3 ChatGPT-4的梦幻联动

核心内容&#xff1a;DALLE 3 & ChatGPT-4的梦幻联动 hello&#xff0c;我是小索奇&#xff0c;最近DALL结合ChatGPT4的话题逐渐上升了起来&#xff0c;今天就带大家探索一下~ DALLE的主要功能是根据文本描述来生成图片。你可以告诉它一个穿着皮草的西瓜&#xff0c;它就能…

叠氮修饰的葡萄糖Ac4GIcNAz,98924-81-3

产品简介&#xff1a;The tetraacetylatedN-Azidoacetyl-glucosamine (Ac4GlcNAz) provides a non-radioactive alternativefor glycoconjugate visualization. It is cell-permeable, intracellularlyprocessed and incorporated instead of its natural monosaccharide count…

MongoDB——window11安装mongodb5.0.21版本服务端(图解版)

目录 一、mongodb官网下载地址二、安装步骤三、配置环境变量四、运行mongodb 一、mongodb官网下载地址 mongodb官网下载地址&#xff1a;https://www.mongodb.com/try/download/community 二、安装步骤 双击运行下载好的mongodb-windows-x86_64-5.0.21-signed.msi安装包&am…

JVM篇---第八篇

系列文章目录 文章目录 系列文章目录一、虚拟机为什么使用元空间替换了永久代?二、什么是Stop The World ? 什么是OopMap?什么是安全点?三、说一下JVM 的主要组成部分及其作用?一、虚拟机为什么使用元空间替换了永久代? 「什么是元空间?什么是永久代?为什么用元空间代…

定时任务详解

1、定时任务 在公司做项目时&#xff0c;经常遇到要用到定时任务的事情&#xff0c;但是对定时任务不熟练的时候会出现重复任务的情况&#xff0c;不深入&#xff0c;这次将定时任务好好学习分析一下 定时任务的原理 假如我将一个现场通过不断轮询的方式去判断&#xff0c;就…

【Gradle-10】不可忽视的构建分析

1、前言 构建性能对于生产力至关重要。 随着项目越来越复杂&#xff0c;花费在构建上的时间就越长&#xff0c;开发效率就越低。 通过分析构建过程&#xff0c;可以了解项目构建的时间都花在哪&#xff0c;以及项目存在哪些潜在的问题&#xff0c;找到构建瓶颈&#xff0c;解…

Qt之实现圆形进度条

在Qt自带的控件中&#xff0c;只有垂直进度条、水平进度条两种。 在平时做页面开发时&#xff0c;有些时候会用到圆形进度条&#xff0c;比如说&#xff1a;下载某个文件的下载进度。 展示效果&#xff0c;如下图所示&#xff1a; 实现这个功能主要由以下几个重点&#xff1a…

在公众号上怎么创建微信付费课程功能呢

微信付费课程功能是一项比较受欢迎的在线教育服务&#xff0c;可以帮助教育机构或个人更好地管理和销售课程资源&#xff0c;提高知识分享和变现的效率。下面将介绍如何创建微信付费课程功能。 一、了解微信付费课程功能 在创建微信付费课程功能之前&#xff0c;需要先了解微信…

【自学笔记】网络安全——黑客技术

想自学网络安全&#xff08;黑客技术&#xff09;首先你得了解什么是网络安全&#xff01;什么是黑客&#xff01;&#xff01;&#xff01; 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队…

【Java 进阶篇】HTML表格标签详解

HTML&#xff08;Hypertext Markup Language&#xff09;表格标签是在网页中用于创建表格的重要工具。表格是一种在网页上以行和列的方式组织和显示数据的有效方式。在本文中&#xff0c;我们将详细介绍HTML表格标签&#xff0c;包括如何创建表格、定义表头、单元格合并等内容。…

Flask实现注册登录模块

&#x1f64c;秋名山码民的主页 &#x1f602;oi退役选手&#xff0c;Java、大数据、单片机、IoT均有所涉猎&#xff0c;热爱技术&#xff0c;技术无罪 &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; 获取源码&#xff0c;添加WX 目录 前言1.…

通过代码优雅地编写图表——Mermaid语法

通过代码优雅地编写图表——Mermaid语法 使用代码&#xff0c;在你的Typora中优雅地编写图表&#xff01; 先看一个示例&#xff1a; gantt dateFormat YYYY-MM-DD title Adding GANTT diagram to mermaid excludes weekdays 2023-01-10section A section Completed task …

快速了解SpringCloud Sleuth --链路追踪 + Zipkin--数据搜集/存储/可视化

&#x1f600;前言 本篇博文是关于SpringCloud Sleuth --链路追踪 Zipkin–数据搜集/存储/可视化的基本介绍和使用&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文…

常见源协议介绍

开源协议&#xff08;Open Source License&#xff09;是一种法律文档&#xff0c;用于规定如何使用、修改和分发开源软件和其他开源项目的规则和条件。这些协议允许创作者或组织将其创造的代码或作品以开放源代码的形式共享给他人&#xff0c;以促进协作、创新和知识共享。常见…

HttpClient实现爬虫开发

网络爬虫是一种高效获取网络信息的方式&#xff0c;而HttpClient是一个强大而灵活的Java库&#xff0c;提供了方便的API和丰富的功能&#xff0c;使其成为开发高效且灵活的网络爬虫的理想选择。本文将分享如何利用HttpClient库进行网络爬虫开发&#xff0c;帮助您更好地理解并实…

NSA 和 CISA 联合揭露当下十大网络安全错误配置

10月5日&#xff0c;美国国家安全局 (NSA) 和网络安全与基础设施安全局 (CISA) 公布了十大目前最常见的网络安全错误配置&#xff0c;这些错误由红蓝团队在大型组织网络中发现。 根据发布的联合报告&#xff0c;团队评估了国防部 (DoD)、联邦民事行政部门 (FCEB)、州和地方政府…

解决程序员百分百问题pip终极杀手

文章目录 pip中国版镜像源永久版中国镜像源pip添加多个中国版镜像源无敌版pip中的小细节 pip中国版镜像源 Python中使用pip安装包&#xff0c;常用的国内镜像有&#xff1a; 例如&#xff1a; Python中使用pip安装包&#xff0c;常用的国内镜像有&#xff1a;清华大学镜像&am…

Mac 点击桌面 出现黑边框 解决

1、桌面黑框效果 2、解决&#xff1a;设置为 仅在台前调度中

电脑散热——液金散热

目录 1.简介 2.传统硅脂与液金导热区别 3.特点 4.优点 5.为什么液金技术名声不太好 6.使用方法 1.简介 凡是对于电脑基础硬件有所了解的人&#xff0c;都知道硅脂是如今高性能电脑设备中必不可少的东西。芯片表面和散热器接触面&#xff0c;虽然肉眼看上去是非常光滑的金属…