线程的使用
目录
1,创建线程的几种方式
2,示例
3,线程常用方法
3.1 std::thread类
3.1.1 成员变量
3.1.2 thread成员函数
3.1.2.1 thread 构造函数
3.1.2.2 thread 析构函数
3.1.2.3 get_id 获取线程id
3.1.2.4 joinable
3.1.2.5 join 加入
3.1.2.6 detach 分离
3.1.2.7 swap 交换线程
3.1.2.8 join用法示例
3.1.2.9 detach()用法示例
3.2 std::this_thread命名空间
3.2.1 this_thread命名空间的函数介绍
3.3 pthread函数 (C++98)
3.3.1 pthread_create 线程创建
3.3.2 pthread_join线程挂起
3.3.3 pthread_exit线程退出
3.3.4 pthread_self获取当前线程id
3.3.5 代码示例
1,创建线程的几种方式
a> 通过函数指针创建线程
b> 通过函数对象创建线程
c> 通过lambda函数创建线程
使用std::thread类创建对象,必须包含头文件
#include <thread>
创建的形式是
std::thread thobj(<CALL_BACK>)
新线程将在创建新对象后立即启动,并将与启动该线程的线程并行执行传递的回调。而且,任何线程都可以通过在该线程的对象上调用join()函数来等待另一个线程退出。
2,示例
std::this_thread::sleep_for 函数是休眠函数,表示当前线程休眠一段时间,休眠期间不与其他线程竞争CPU
std::chrono::seconds 是 std::chrono 库中的一个持续时间类
std::cref、std::ref 和 std::reference_wrapper
在 C++ 编程中,有时候我们需要在不进行拷贝的情况下传递引用,或者在需要引用的地方使用常量对象。为了解决这些问题,C++ 标准库提供了三个有用的工具:std::cref、std::ref 和 std::reference_wrapper。
std::cref 是一个模板函数,用于创建对常量对象的引用。它返回一个 std::reference_wrapper 对象,可以在需要引用的地方使用。这在函数参数传递中特别有用,因为它允许我们在不进行拷贝的情况下传递常量对象,同时保持引用的语义。
std::ref 是一个模板函数,用于创建对可修改对象的引用。它返回一个 std::reference_wrapper 对象,允许我们在需要引用的地方使用,同时允许修改被引用的对象。
std::reference_wrapper 是一个模板类,用于包装引用,使其能够在容器中存储或以引用的形式传递。它提供类似引用的语法,并且可以与标准容器一起使用,因为容器无法直接存储引用。
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;class Thread_Test
{
public:void operator()(){for (int i = 0; i < 5; i++){cout << "class Thread_Test is running..." << endl;std::this_thread::sleep_for(std::chrono::seconds(2));}}void FunctionInner(int m){for (int j = 0; j < m; j++){cout << "class FunctionInner thread is running..." << endl;std::this_thread::sleep_for(std::chrono::seconds(2));}}
};void thread_func( int m )
{for (int i = 0; i < m; ++i) {cout << " thread thread_func is running..." << endl;std::this_thread::sleep_for(std::chrono::seconds(1));}
}int main()
{// 1.普通函数int nPara = 3;std::thread obj_thread1(thread_func, std::cref(nPara));cout << "thread_func() thread running..." << endl;obj_thread1.join();cout << "main enter class thread" << endl;// 2.仿函数创建// 方法一/* thread obj_thread((Thread_Test())); */// 方法二Thread_Test c_thread2;thread obj_thread(c_thread2);cout << "main thread running..." << endl;obj_thread.join();cout << "main enter thread2 running..." << endl;// 普通成员函数Thread_Test c_thread3;int num = 2;thread obj_thread2(&Thread_Test::FunctionInner, &c_thread3, std::cref(num));obj_thread2.join();cout << "lambda thread is entry" << endl;// 3.lambda 表达式创建std::thread obj_thread3([]{for (int i = 0; i < 3; i++){cout << "lambda thread is running..." << endl;std::this_thread::sleep_for(std::chrono::seconds(3));}});obj_thread3.join();cout << "main thread is exit" << endl;return 0;
}
3,线程常用方法
<thread>头文件包含了std::thread类和std::this_thread命名空间的声明。C++开发中include <thread>头文件,就可以使用std:thread线程类和std::this_thread命名空间,std::this_thread这个命名空间包含了对当前线程的一些基本操作,如获取当前线程id、休眠当前线程、让渡当前线程的时间片给其他线程等。
3.1 std::thread类
std::thread类来表示执行的各个线程。执行线程是实际上是执行一系列指令,可以在多线程环境中与其他此类序列同时执行,同时共享相同的地址空间。
一个初始化的线程(std::thread)对象表示活动的执行线程,即初始化后std::thread立即运行;这样的线程对象是可连接的(joinable),并且具有唯一的线程id。默认构造的(未初始化的)线程对象是不可连接的(not joinable),其线程id对于所有不可连接线程都是通用的。
如果move线程,或者对它们调用join或detach,则可连接线程将变得不可连接。
3.1.1 成员变量
id
Thread id (public member type)
native_handle_type
Native handle type (public member type)
示例:
#include <thread>
#include <chrono>
#include <iostream>
using namespace std;void thread_func(int m)
{for (int i = 0; i < m; ++i) {cout << " thread thread_func is running..." << endl;std::this_thread::sleep_for(std::chrono::seconds(1));}
}int main()
{int nPara = 3;std::thread obj_thread1(thread_func, std::cref(nPara));// thread 成员变量 idstd::thread::id f_id = obj_thread1.get_id();cout << "thread_func() thread running..." << endl;obj_thread1.join();cout << "main exit thread" << endl;return 0;
}
3.1.2 thread成员函数
Construct thread // 构造函数
thread destructor // 析构函数
operator=// 赋值重载
get_id // 获取线程id
joinable// 判断线程是否可以加入等待
join//Join thread (public member function ) 加入等待
detach//Detach thread (public member function ) 分离线程
swap//Swap threads (public member function ) 线程交换
native_handle// 获取线程句柄
hardware_concurrency // 检测硬件并发特性
swap (thread)
Swap threads (function )
3.1.2.1 thread 构造函数
a> 默认构造函数,创建一个空的 thread 执行对象。
b> 普通构造函数,初始化构造函数,创建一个 thread对象,该 thread对象可被 joinable,新产生的线程会调用 fn 函数,该函数的参数由 args 给出。
c> 拷贝构造函数(被禁用),意味着 thread 不可被拷贝构造。
d> move 构造函数(移动分配线程函数),move 构造函数,调用成功之后 x 不代表任何 thread 执行对象。
拷贝构造函数 (被禁)
// 函数原型
thread(const thread&) = delete;
thread& operator=(const thread&) = delete;// 错误 示例代码
thread t3(t1);
thread t4 = t1;
移动分配线程函数
// 代码原型
thread& operator=(thread&& _Other) noexcept
thread(thread&& _Other) noexcept
// 正确的示例代码
thread t5 = std::move(t1);
thread t6(std::move(t1));
示例:
#include <thread>
#include <chrono>
#include <iostream>
using namespace std;class Thread_Test
{
public:void operator()(){for (int i = 0; i < 5; i++){cout << "class Thread_Test is running..." << endl;std::this_thread::sleep_for(std::chrono::seconds(2));}}void FunctionInner(int m){for (int j = 0; j < m; j++){cout << "class FunctionInner thread is running..." << endl;std::this_thread::sleep_for(std::chrono::seconds(2));}}
};void thread_func(int m)
{for (int i = 0; i < m; ++i) {cout << " thread thread_func is running..." << endl;std::this_thread::sleep_for(std::chrono::seconds(1));}
}int main()
{int nPara = 3;// a, 默认构造函数std::thread t1; // b, 构造函数构造/* 通过值构造 */std::thread t2(thread_func, nPara);/* 通过引用构造 */std::thread t3(thread_func, std::cref(nPara));/* 仿函数指针构造 */std::thread t4((Thread_Test()));/* 通过仿函数对象构造 */Thread_Test c_thread2;std::thread t5(c_thread2);/* 通过普通成员函数构造 */Thread_Test c_thread3;int num = 2;std::thread t6(&Thread_Test::FunctionInner, &c_thread3, std::cref(num));// d, move// t7 is now running . t3 is no longer a threadstd::thread t7(std::move(t3));t2.join();t4.join();t5.join();t6.join();t7.join();cout << "main exit class thread" << endl;return 0;
}
3.1.2.2 thread 析构函数
当线程对象被销毁时,它会自动调用析构函数,如果线程没有被join()或detach(),则程序会终止并抛出std::terminate异常。
// 不调用join()或detach()
// 当my_thread对象离开作用域时会抛出std::terminate异常
std::thread t2(thread_func, nPara);
3.1.2.3 get_id 获取线程id
cout << "thread1' id is " << t1.get_id() << endl;
3.1.2.4 joinable
判断线程是否可连接
bool joinable() const noexcept;
检查线程是否可连接,返回线程对象是否可连接。
(1)如果线程对象表示执行线程,则它是可连接的。
(2)在以下任何情况下,线程对象都不可连接:
a, 如果它是默认构造的。
b, 如果它已被move(构造另一个线程对象,或分配给另一个线程)。
c, 如果已调用其成员join或detach。
示例:
if (false == t7.joinable())
{cout << "[error] : t7 is not join! " << endl;
}
else
{cout << "t7 is join... " << endl;t7.join();
}
3.1.2.5 join 加入
连接线程,阻塞调用线程
void join();
连接(join)线程,当线程执行完成时,函数返回。
join()函数能够保证调用线程和被调用线程同步,join()函数将阻塞调用该join()函数线程的执行,直到被调用线程的函数返回。调用join()函数后,线程对象变得不可连接,可以安全销毁。
3.1.2.6 detach 分离
分离线程,调用线程和被调用线程各自独立运行
void detach();
分离(detach)线程将对象表示的线程与调用线程分离,允许它们彼此独立地执行。
这两个线程都继续运行,不会以任何方式阻塞或同步。请注意,当其中一方结束执行时,其资源将被释放。调用此函数后,线程对象变得不可连接,可以安全销毁。
3.1.2.7 swap 交换线程
void swap (thread& x) noexcept;
交换线程,将对象的状态与x的状态交换。
swap函数与thread的移动构造函数和移动赋值函数作用一样。
示例
cout << "thread1' id is " << t1.get_id() << endl;
cout << "thread2' id is " << t2.get_id() << endl;cout << "swap after:" << endl;
swap(t1, t2);//线程交换
cout << "thread1' id is " << t1.get_id() << endl;
cout << "thread2' id is " << t2.get_id() << endl;
3.1.2.8 join用法示例
#include <iostream> // std::cout
#include <thread> // std::thread
void foo()
{ // do stuff... }void bar(int x)
{ // do stuff... }int main()
{std::thread first (foo); std::thread second (bar,0); first.join(); // pauses until first finishessecond.join(); // pauses until second finishes return 0;
}
3.1.2.9 detach()用法示例
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::sleep_for
#include <chrono> // std::chrono::secondsvoid pause_thread(int n)
{std::this_thread::sleep_for (std::chrono::seconds(n));std::cout << "pause of " << n << " seconds ended\n";
}int main()
{std::cout << "Spawning and detaching 3 threads...\n";std::thread (pause_thread,1).detach();std::thread (pause_thread,2).detach();std::thread (pause_thread,3).detach();std::cout << "Done spawning threads.\n";std::cout << "(the main thread will now pause for 5 seconds)\n";// give the detached threads time to finish (but not guaranteed!):pause_thread(5);return 0;
}
输出:
Spawning 5 threads...
Done spawning threads. Now waiting for them to join:
pause of 1 seconds ended
pause of 2 seconds ended
pause of 3 seconds ended
pause of 4 seconds ended
pause of 5 seconds ended
All threads joined!
3.2 std::this_thread命名空间
此命名空间提供了访问当前线程的一组函数。
3.2.1 this_thread命名空间的函数介绍
get_id 获得当前线程id
Yield 将当前线程时间片让渡给其他线程
sleep_until 当前线程休眠直到某个时间点
sleep_for 当前线程休眠一段时间
3.3 pthread函数 (C++98)
3.3.1 pthread_create 线程创建
函数原型
int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict attr,void *(*start_rtn)(void),void *restrict arg);
返回值:若是成功建立线程返回0,否则返回错误的编号。
形式参数:pthread_t *restrict tidp要创建的线程的线程id指针;
const pthread_attr_t *restrict attr创建线程时的线程属性;
void* (start_rtn)(void)返回值是void类型的指针函数;
void *restrict arg start_rtn的形参。
3.3.2 pthread_join线程挂起
该函数的作用使得当前线程挂起,等待另一个线程返回才继续执行。也就是说当程序运行到这个地方时,程序会先停止,然后等线程id为thread的这个线程返回,然后程序才会断续执行。
函数原型:
int pthread_join( pthread_t thread, void **value_ptr);
参数说明如下:thread等待退出线程的线程号;
value_ptr退出线程的返回值。
3.3.3 pthread_exit线程退出
函数原型
void pthread_exit(void *rval_ptr);
3.3.4 pthread_self获取当前线程id
函数原型
pthread_t pthread_self(void);
3.3.5 代码示例
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
void *thread(void *ptr)
{int i;for(i=0;i<3;i++){sleep(1);printf("This is a pthread.\n");}
}
int main(void)
{pthread_t id;int i,ret;ret=pthread_create(&id,NULL,thread,NULL);if(ret!=0){printf ("Create pthread error!\n");exit (1);}for(i=0;i<3;i++){printf("This is the main process.\n");sleep(1);}pthread_join(id,NULL);return (0);
}编译链接命令 g++ -o example2 example.c -lpthread
相关文章:
线程的使用
目录 1,创建线程的几种方式 2,示例 3,线程常用方法 3.1 std::thread类 3.1.1 成员变量 3.1.2 thread成员函数 3.1.2.1 thread 构造函数 3.1.2.2 thread 析构函数 3.1.2.3 get_id 获取线程id 3.1.2.4 joinable 3.1.2.5 join 加入 …...
flutter选择国家或地区的电话号码区号
1.国家区号列表(带字母索引侧边栏) import package:generated/l10n.dart; import package:widget/login/area_index_bar_widget.dart; import package:flutter/material.dart; import package:flutter_screenutil/flutter_screenutil.dart;class LoginA…...
信号隔离器在PLC/DCS控制系统的应用
彭姝麟 Acrelpsl 概述: 随着工业自动化程度的不断提高,变频器也得到了非常广泛的应用。作为电力电子器件,变频器中要进行大功率二极管整流,大功率晶体管变压,在输入输出回路产生电流高次谐波,干扰供电系统、负载以及附…...
探索Linux世界:基本指令(文件查看、时间相关、grep、打包压缩及相关知识)
今天继续介绍一些指令 文章目录 1.cat - 查看文件1.1输出重定向和追加重定向1.2指令echo 2.more 指令3.less - 逐页查看文本文件内容4.head- 显示文件开头部分内容5.tail - 显示文件末尾部分内容5.1输入重定向(<)5.2管道(|) 6.…...
简单使用国产数据库—达梦
达梦数据库是一款由中国的达梦软件公司开发的关系数据库管理系统(RDBMS),它在业界以其高性能、可扩展性和安全性而著称。该系统广泛应用于各种应用程序的数据存储和管理,满足用户对于数据处理和管理的多样化需求。 安装好的达梦数…...
STM32点亮LED灯与蜂鸣器发声
STM32之GPIO GPIO在输出模式时可以控制端口输出高低电平,用以驱动Led蜂鸣器等外设,以及模拟通信协议输出时序等。 输入模式时可以读取端口的高低电平或电压,用于读取按键输入,外接模块电平信号输入,ADC电压采集灯 GP…...
Android UI: 自定义控件:可换行的布局控件
文章目录 继承ViewGroup重写onMeasure方法:计算并设置布局控件的高度重写onLayout方法:计算并设置每个子控件的位置具体的代码实现小结 继承ViewGroup 重写generateLayoutParams,设置子控件的LayoutParams为MarginLayoutParams类型 Overridep…...
Linux(Ubuntu)中安装vscode
①首先去vscode的官网下载.deb文件 网址:https://code.visualstudio.com/docs/?dvlinuxarm64_deb 注:如果linux端无法打开网页下载文件,可以在Windows端下载好用WinSCP传输到Linux。下载前注意下你的系统架构是arm还是amd,系统…...
MQTT Topic通配符
🌹作者主页:青花锁 🌹简介:Java领域优质创作者🏆、Java微服务架构公号作者😄 🌹简历模板、学习资料、面试题库、技术互助 🌹文末获取联系方式 📝 往期热门专栏回顾 专栏…...
负载均衡 dubbo
1 自定义负载均衡 dubbo 在 Dubbo 中,用户可以自定义负载均衡策略以满足特定场景的需求。Dubbo 提供了扩展接口 com.alibaba.dubbo.rpc.cluster.LoadBalance 来支持自定义负载均衡算法。 要实现自定义的负载均衡策略,需要完成以下步骤: 创建…...
(含代码)利用NVIDIA Triton加速Stable Diffusion XL推理速度
在 NVIDIA AI 推理平台上使用 Stable Diffusion XL 生成令人惊叹的图像 扩散模型正在改变跨行业的创意工作流程。 这些模型通过去噪扩散技术迭代地将随机噪声塑造成人工智能生成的艺术,从而基于简单的文本或图像输入生成令人惊叹的图像。 这可以应用于许多企业用例&…...
【Spring】学习Spring框架那点小事儿
Spring作者:Rod Johnson Rod Johnson 是一位软件开发人员和作家,他在软件开发领域有着广泛的影响力。他出生于澳大利亚,拥有计算机科学和音乐双学位(能写出有优雅的代码一定有艺术细胞)。 Rod Johnson 在 2002 年出版…...
L2-035 完全二叉树的层序遍历(Python)
L2-035 完全二叉树的层序遍历 分数 25 全屏浏览 切换布局 作者 陈越 单位 浙江大学 一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于深度为 D 的,有 N 个结点的二叉树,若其结点对应于相同深度…...
get命令使用提交代码
当你想要通过Git提交代码时,以下是一个详细的案例,包括从创建更改到推送到远程仓库的整个过程: 首先,确保你已经在本地仓库目录中进行了需要的更改。 添加更改到暂存区: git add . 这会将所有更改添加到Git的暂存区&…...
矩阵乘积知识
参考:矩阵点乘【矩阵点乘计算公式】_万动力 矩阵乘 矩阵相乘最重要的方法是一般矩阵乘积。它只有在第一个矩阵的列数(column)和第二个矩阵的行数(row)相同时才有意义 [1] 。 哈达码积 别名:矩阵点乘&…...
10、设计模式之外观模式(Facade)
一、什么是外观模式 这个大家一定是经常使用的,外观模式(门面模式)是一种结构型设计模式。它提供一个统一的接口,用于访问子系统中的一组接口,隐藏了系统的复杂性。最简单的应用就是,当controller层的逻辑处…...
小程序APP为什么要选择游戏盾SDK防护DDOS
小程序APP为什么要选择游戏盾SDK防护DDOS?在移动互联网高速发展的今天,小程序APP已经成为了人们日常生活中不可或缺的一部分。无论是购物、娱乐还是社交,小程序APP都为我们提供了极大的便利。然而,随着小程序APP的普及,…...
STL之deque容器代码详解
1 基础概念 功能: 双端数组,可以对头端进行插入删除操作。 deque与vector区别: vector对于头部的插入删除效率低,数据量越大,效率越低。 deque相对而言,对头部的插入删除速度回比vector快。 vector访问…...
Liunx文件系统和基础IO
文件系统和基础IO 基础IOc语言基础IO函数当前路径和标准流系统IO系统调用函数重定向FILE文件结构体 在谈缓存区问题理解文件系统初识inode 基础IO c语言基础IO函数 打开与关闭 FILE *fopen(char *filename, const char *mode);选项还可以是 r/w/a 意味着为可读可写打开。 2…...
【Python+Selenium学习系列5】Selenium特殊元素定位之-鼠标悬停操作
前言 Selenium模拟用户在浏览器中的操作,比如点击按钮。在某些场景下,我们需要模拟鼠标悬停的操作,来触发一些隐藏的元素。本文将介绍Python Selenium实现鼠标悬停操作。 鼠标悬停,即当光标与其名称表示的元素重叠时触发的事件&…...
接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...
P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
Ascend NPU上适配Step-Audio模型
1 概述 1.1 简述 Step-Audio 是业界首个集语音理解与生成控制一体化的产品级开源实时语音对话系统,支持多语言对话(如 中文,英文,日语),语音情感(如 开心,悲伤)&#x…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
Kafka入门-生产者
生产者 生产者发送流程: 延迟时间为0ms时,也就意味着每当有数据就会直接发送 异步发送API 异步发送和同步发送的不同在于:异步发送不需要等待结果,同步发送必须等待结果才能进行下一步发送。 普通异步发送 首先导入所需的k…...
