C++ thread线程库

news2025/1/10 20:54:39

thread库

std::thread 是 C++ 标准库中的一个类,用于管理和控制单个执行线程。线程允许程序并行执行多个函数,从而提高性能和响应速度。std::thread 类提供了一种便捷的方式来创建和操作线程。

1、用途

并行执行任务: 通过 std::thread,可以同时执行多个函数。

多任务处理: 可以在不同的线程中处理不同的任务,使得程序可以同时完成多个任务。

资源共享和同步: 通过 std::thread,多个线程可以共享资源,如内存和文件。为避免数据竞争和冲突,通常会使用同步机制(如 std::mutexstd::atomic)来保护共享资源。

2、关键点

线程创建和启动std::thread 在构造时立即开始执行提供的函数。

线程同步joinable() 方法检查线程是否可以被 joindetach。调用 join() 会阻塞当前线程,直到目标线程完成。

3、线程的状态
  • 默认构造、移动构造、detachjoin 后,std::thread 对象将不再表示任何线程。
  • 两个 std::thread 对象不能表示同一执行线程,且 std::thread 不能复制,但可以移动。

一、类成员

id: 表示线程的id

std::thread::id
线程id唯一表示一个线程,一旦线程结束,这个id可以复用作为另一个线程的线程id;

在这里插入图片描述

二、成员函数

1、构造函数

用于构造新的thread对象

函数原型标准
thread() noexcept;(1)(C++11 起)
thread( thread&& other ) noexcept;(2)(C++11 起)
template< class Function, class… Args > explicit thread( Function&& f, Args&&… args );(3)(C++11 起)
thread(const thread&) = delete;(4)(C++11 起)

(1) 默认构造函数:构造一个不表示新线程thread对象,即不创建新线程。

(2) 移动构造函数:将other表示的执行线程的所有权转移给新创建的线程对象thread。调用后,thread表示执行线程,而other不再表示任何线程。

(3) 函数对象和参数列表:使用给定的函数和该函数的参数列表来创建并启动一个新线程。

(4) 复制构造函数:此构造函数被删除,因此thread对象不允许复制。

1)代码测试
#include <iostream>
#include <thread>
#include <chrono>
#include <string>

void func(int num,std::string str)
{
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << str <<" " << num << ":" << std::this_thread::get_id() << std::endl;
}

void func2()
{
    std::cout << "is func2" << std::endl;
}

int main()
{
    /*未启动线程*/
    // std::thread thread1; 
    // if(thread1.joinable())
    // {
    //     std::cout << "thread1 线程可被释放" << std::endl;
    //     thread1.join(); 
    // } else{
    //     std::cout << "thread1 线程不可被释放" << std::endl;
    // }

    //std::thread thread();
    // if(thread.joinable())    // 非法使用
    // {
    //     thread.join();   // 非法使用
    // }

    /*构造并启动*/
    // std::thread thread2(func,2,"thread2");
    // if(thread2.joinable())
    // {
    //     std::cout << "thread2 线程可被释放" << std::endl;
    //     thread2.join(); 
    // } else{
    //     std::cout << "thread2 线程不可被释放" << std::endl;
    // }    

    /*使用可调用对象和参数列表*/ 
    // std::thread thread4(func,4,"thread4");
    // if(thread4.joinable())
    // {
    //     thread4.join();
    //     std::cout << "t3 线程被释放" << std::endl;
    // } else{
    //     std::cout << "t3 线程不能被释放" << std::endl;
    // }

    /*移动构造*/ 
    // std::thread thread3(func,3,"thread3");
    // std::thread move_thread = std::move(thread3);
    // if(thread3.joinable())
    // {
    //     thread3.join();
    //     std::cout << "t3 线程被释放" << std::endl;
    // } else{
    //     std::cout << "t3 线程不能被释放" << std::endl;
    // }

    // if(move_thread.joinable())
    // {
    //     move_thread.join();
    //     std::cout << "move_thread 线程被释放" << std::endl;
    // } else{
    //     std::cout << "move_thread 线程不能被释放" << std::endl;
    // }

    /*复制构造*/
    // std::thread thread5(func,5,"thread5");
    // std::thread copy_thread(thread5);
    // thread5.join();
    // if(copy_thread.joinable())
    // {
    //     copy_thread.join();
    //     std::cout << "copy_thread 线程被释放" << std::endl;
    // } else{
    //     std::cout << "copy_thread 线程不能被释放" << std::endl;
    // }

    /*使用默认构造函数,延时启动线程*/
    // std::thread thread6; // 默认构造
    // thread6 = std::thread(func,6,"thread6");
    // if(thread6.joinable())
    // {
    //     thread6.join();
    //     std::cout << "thread6 线程被释放" << std::endl;
    // } else{
    //     std::cout << "thread6 线程不能被释放" << std::endl;
    // }

    return 0;
}
2)执行结果

在这里插入图片描述

2、析构函数

用于销毁 thread 对象,析构之前一定要保证没有正在运行的关联线程( 即joinable()返回true ),否则调用析构函数时会导致调用std::terminate(),从而终止程序 ,产生资源泄露和未定义行为。

注: std::terminate 函数 是一个用于处理未捕获异常的全局终止函数。当程序遇到未捕获的异常或者某些致命错误时,std::terminate 会被调用,通常会导致程序非正常退出。

函数原型标准
~thread();(C++11起)

在下列操作后 thread 对象无关联的线程(从而可安全销毁)

1.被默认构造,并没有被启动的线程对象
std::thread thread1;	// thread1没有被启动,则可以被安全销毁

2.被移动构造的线程对象
std::thread t2(func, argv);
std::thread t3(std::move(t2)); // 线程对象 t2 被移动后可以被安全销毁

3.调用了join()后的线程对象
t3.join();				// 线程对象 t3再调用 join()后,可以被安全销毁

4.调用了detach()后的线程对象
std::thread t4(func,argv);
t4.detach();			// 线程对象 t4 在调用detach()后可以被安全销毁

经过这些操作后,std::thread 对象不再与任何线程关联,可以安全销毁。这些操作确保在销毁 std::thread 对象时不会调用 std::terminate()

1)代码测试
#include <iostream>
#include <thread>
#include <chrono>

void func(std::string str)
{
    std::cout << str << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
}

int main()
{
    // std::thread t1(func,"t1");
    std::thread t2(func,"t2");
    std::thread t3(std::move(t2));
    std::thread t4(func,"t4");
    t3.join();
    t4.detach();
    return 0;
}
2)执行结果

在这里插入图片描述

3、运算符重载operator=

operator= 实际上是一个移动赋值运算符。它执行的是移动语义,将一个线程对象的所有权从一个 std::thread 对象转移到另一个 std::thread 对象。,类比移动构造。

4、joinable

检查 std::thread 对象是否活着。 若 thread 对象表示的执行线程活着则返回 true ,否则返回 false 。

函数原型标准
bool joinable() const noexcept;(C++11起)
1)代码测试
std::thread t1(func,argc);
if(t1.joinable())
	std::cout << "线程存活" << std::endl;
else
	std::cout << "线程死亡" << std::endl;

t1.join();
if(t1.joinable())
	std::cout << "线程存活" << std::endl;
else
	std::cout << "线程死亡" << std::endl;
2)执行结果

在这里插入图片描述

5、get_id

获取与线程对象关联的线程的 ID 。

  • std::this::thread::get_id(); 获取当前执行线程的ID;
  • std::thread 对象调用 get_id(),获取与该对象关联的线程ID;
函数原型标准
[std::thread::id]get_id() const noexcept;(C++11 起)
1)代码测试
#include <iostream>
#include <thread>
#include <thread>
#include <mutex>    // 互斥锁头文件

std::mutex mutex_lock;  // 定义互斥锁

void func(std::string str)
{
    std::lock_guard<std::mutex> lock(mutex_lock);  // 确保线程安全
    std::cout << "线程 " << str << " id = " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2)); 
}

int main()
{
    
    std::thread t1(func,"t1");
    std::thread t2(func,"t2");
    {
        std::lock_guard<std::mutex> lock(mutex_lock);  // 确保线程安全
        std::thread::id t1_id = t1.get_id();
        std::thread::id t2_id = t2.get_id();
        std::cout << "t1_id = " << t1_id << std::endl;
        std::cout << "t2_id = " << t2_id << std::endl;    
    }
    
    {
        std::lock_guard<std::mutex> lock(mutex_lock);  // 确保线程安全
        std::cout << "主线程 " << " id = " << std::this_thread::get_id() << std::endl;
    }
    t1.join();
    t2.join();
    return 0;
}

注:mutex_lock 是线程互斥锁,防止控制台输出竞争,输出信息混乱。关注get_id()函数的调用即可,使用互斥锁,是为了让了输出更美观,并获得正确的输出信息。

2)执行结果

在这里插入图片描述

在这里插入图片描述

6、native_handle

用于获取与 std::thread 对象关联的底层线程句柄。 std::thread 提供了一组基本的线程管理功能,但是在某些情况下,可能需要更低级的线程管理功能,例如设置线程优先级、绑定线程到特定的 CPU 核心、或其他特定于平台的操作。 如果需要与平台特定的 API 交互,native_handle() 可以获取底层线程句柄,从而进行更精细的控制。

函数原型标准
native_handle_type native_handle();(C++11 起)(可选)
1)代码测试
#include <iostream>
#include <thread>
#include <pthread.h>    // c线程库
#include <errno.h>
#include <string.h>

void func()
{
    std::cout << "thread ID = " << std::this_thread::get_id() << std::endl;
}

int main()
{
    std::thread t(func);
    pthread_t nativeHandle = t.native_handle(); // 获取底层线程句柄

    // 设置线程优先级
    sched_param sch_params;
    sch_params.sched_priority = 20; // 设置优先级
    if (pthread_setschedparam(nativeHandle, SCHED_FIFO, &sch_params)) 
    {
        std::cerr << "设置线程调度失败: " << strerror(errno) << std::endl;
    }
    t.join();
    return 0;
}
2)执行结果

在这里插入图片描述

注:普通用户没有权限设置优先级,可以使用 sudo 获取临时管理员权限运行。

7、hardware_concurrency

返回支持的并发线程数,这个返回值因为系统资源限制、竞争条件、硬件支持等情况,该值只能做一个参考。

函数原型标准
static unsigned int hardware_concurrency() noexcept;(C++11 起)
1)代码测试
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutex_lock;

void func(std::string str)
{
    std::lock_guard<std::mutex> lock(mutex_lock);  // 确保线程安全
    std::cout << "线程 " << str << " id = " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2)); 
}

int main()
{
    static unsigned int num = std::thread::hardware_concurrency();
    std::cout << "支持" << num << "个线程" << std::endl;
    return 0;
}
2)执行结果

在这里插入图片描述

8、join

用于阻塞当前线程,直到被 join() 的线程执行结束,被 join 的线程执行结束后,从对应的 join() 处返回。多个线程内,对同一个 std::thread 对象调用 join() 会导致未定义行为。

函数原型标准
void join();(C++11 起)

注: std::thread 对象调用 join()后,joinable() 结果为 false

1)代码测试
#include <iostream>
#include <thread>
#include <chrono>

void func(std::string str)
{
    std::cout << str << " " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
}

void func1(std::string str)
{
    std::cout << str << " " << std::this_thread::get_id() << std::endl;
}

int main()
{
    std::thread t(func,"线程t");
    t.join();
    std::cout << "主线程: " << std::this_thread::get_id() << std::endl;

    std::thread t1(func1,"线程t1");
    std::thread t2([&t1]{
        t1.join();
    });
    std::thread t3([&t1] {
        t1.join();
    });
    t2.join();
    t3.join();
    return 0;
}
2)执行结果

在这里插入图片描述

9、detach

从 std::thread 对象分离执行线程,允许被分离的线程独立持续执行。该线程退出会自行释放资源,调用 detach 不再占有任何线程。

函数原型标准
void detach();(C++11 起)
1)代码测试
#include <iostream>
#include <thread>
#include <chrono>

void func(std::string str)
{
    std::cout << str << " " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
    std::thread t(func,"线程t");
    std::this_thread::sleep_for(std::chrono::seconds(2));
    t.detach();
    
    if(t.joinable())
    {
        std::cout << "线程t可被释放" << std::endl;
    }else{
        std::cout << "线程t自行释放" << std::endl;
    }
    return 0;
}
2)执行结果

在这里插入图片描述

10、swap

用于交换两个底层 std::thread 对象的句柄。

函数原型标准
void swap( [std::thread]& other ) noexcept;(C++11 起)
1)代码测试
#include <iostream>
#include <thread>
#include <chrono>

void func(std::string str)
{
    std::cout << str << " " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
}

void func1(std::string str)
{
    std::cout << str << " " << std::this_thread::get_id() << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(2));
}

int main()
{
    std::thread t1(func,"t1");
    std::cout << "t1_ID = " << t1.get_id() << std::endl;
    std::thread t2(func1,"t2");
    std::cout << "t2_ID = " << t2.get_id() << std::endl;
    t1.swap(t2);
    std::cout << "t1_ID = " << t1.get_id() << std::endl;
    std::cout << "t2_ID = " << t2.get_id() << std::endl;
    t1.join();
    t2.join();
    return 0;
}
2)执行结果

在这里插入图片描述

参考资料:[ [std::thread - C++中文 - API参考文档 ] ]( std::thread - C++中文 - API参考文档 (apiref.com) )
如有谬误,请各位大佬指正。

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

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

相关文章

守护家庭的安全卫士:家用可燃气体探测器

在这个追求智能与安全并重的时代&#xff0c;每一个细微之处的防护都显得尤为重要&#xff0c;尤其是在我们最为依赖的家庭空间里。当谈及家居安全&#xff0c;燃气安全无疑占据着至关重要的位置。据统计&#xff0c;每年因燃气管老化、连接处松动等问题引发燃气泄漏的事故不在…

外泌体相关基因肝癌临床模型预测——2-3分纯生信文章复现——4.预后相关外泌体基因确定单因素cox回归(2)

内容如下&#xff1a; 1.外泌体和肝癌TCGA数据下载 2.数据格式整理 3.差异表达基因筛选 4.预后相关外泌体基因确定 5.拷贝数变异及突变图谱 6.外泌体基因功能注释 7.LASSO回归筛选外泌体预后模型 8.预后模型验证 9.预后模型鲁棒性分析 10.独立预后因素分析及与临床的…

STM32-HAL-FATFS(文件系统)(没做完,stm32f103zet6(有大佬的可以在评论区说一下次板子为什么挂载失败了))

1STM32Cube配置 1-1配置时钟 1-2配置调试端口 1-3配置uart 1-4配置SDIO&#xff08;注意参数&#xff09;&#xff08;其中他的初始化的异常函数给注释&#xff0c;SD卡文件写了&#xff09; 配置了还要打开中断和DMA可在我的其他文章中看一样的 1-5配置FatFs (只改了图选中…

Unity 资源 之 Sweet Cakes Icon套装,110个高品质蛋糕图标分享

Sweet Cakes Icon 套装 - 为 Unity 开发者带来甜蜜惊喜 前言资源包内容领取兑换码 前言 亲爱的 Unity 开发者们&#xff0c;今天要向你们介绍一款令人心动的图标套装 - Sweet Cakes Icon。 Sweet Cakes Icon 套装包含了超过 110 种高品质的蛋糕和纸杯蛋糕图标&#xff0c;这无…

累积分布函数的一些性质证明

性质1&#xff1a; E [ X ] ∫ 0 ∞ ( 1 − F ( x ) ) d x − ∫ − ∞ 0 F ( x ) d x ( 1 ) E[X]\int_0^{\infty}(1-F(x))dx - \int_{-\infty}^0F(x)dx\quad (1) E[X]∫0∞​(1−F(x))dx−∫−∞0​F(x)dx(1) 证明&#xff1a; E [ X ] ∫ − ∞ ∞ x p ( x ) d x E[X] …

一个人的开发团队:前后端与调动AI

作为一名大数据开发者&#xff0c;我对 AI 的应用感兴趣&#xff0c;但平常都是处理数据&#xff0c;对应前后端代码不擅长&#xff0c;幸好有了 AI&#xff0c;在 AI 的帮助下能写出很多前后端代码了。 目录 前端开发后端开发调用AI整合前后端与AI 本文将通过一个简单的项目…

GO语言入门之准备

一、Go的简介 1.什么是Go Go 是一个开源的编程语言&#xff0c;最早起源于2007年&#xff0c;在2009年正式对外发布&#xff0c;Go 语言被设计成一门应用于搭载 Web 服务器&#xff0c;存储集群或类似用途的巨型中央服务器的系统编程语言。它能让构造简单、可靠且高效的软件变…

国产麒麟v10、UOS系统在线比较两个Word文件的内容差异

调用PageOffice的WordCompare方法&#xff0c;同时在线打开两个Word文档&#xff0c;可以切换显示其中的一个文档&#xff0c;或者显示两个文档的对比结果&#xff0c;即可实现在线的文档内容比较功能。此功能可以应用在以下方面&#xff1a; 文档管理中&#xff0c;比较两个版…

Day05-01-jenkins进阶

Day05-01-jenkins进阶 10. 案例07: 理解 案例06基于ans实现10.1 整体流程10.2 把shell改为Ansible剧本10.3 jk调用ansible全流程10.4 书写剧本 11. Jenkins进阶11.1 jenkins分布式1&#xff09;概述2&#xff09;案例08&#xff1a;拆分docker功能3&#xff09;创建任务并绑定到…

AI绘画擦边变现赛道怎么玩?新手小白必看教程!

今天给大家介绍一个用 AI 搞擦边的变现赛道 而且可以说是0 成本变现的 现在真的越来越多的人都想 0 成本变现&#xff0c;那么 0 成本到底能不能变现&#xff0c;变现的上下限又是多少&#xff1f; 今天这个案例就可以很好的进行说明 可以说 AI 是现在第一生产力&#xff0…

代码随想录——柠檬水找零(Leetcode860)

题目链接 贪心 class Solution {public boolean lemonadeChange(int[] bills) {if(bills[0] 10 || bills[0] 20 || bills[1] 20){return false;}int count5 1;int count10 0;for(int i 1; i < bills.length; i){if(bills[i] 5){count5;}if(bills[i] 10){count10;…

VBA常用的字符串内置函数

前言 在VBA程序中&#xff0c;常用的内置函数可以按照功能分为字符串函数、数字函数、转换函数等等&#xff0c;本节主要会介绍常用的字符串的内置函数&#xff0c;包括Len()、Left()、Mid()、Right()、Split()、String()、StrConV()等。 本节的练习数据表以下表为例&#xff…

Mybatis实现RBAC权限模型查询

RBAC权限模型 Role-Based Access Control&#xff0c;中文意思是&#xff1a;基于角色&#xff08;Role&#xff09;的访问控制。这是一种广泛应用于计算机系统和网络安全领域的访问控制模型。 简单来说&#xff0c;就是通过将权限分配给➡角色&#xff0c;再将角色分配给➡用…

leetcode--二叉树中的最长交错路径

leetcode地址&#xff1a;二叉树中的最长交错路径 给你一棵以 root 为根的二叉树&#xff0c;二叉树中的交错路径定义如下&#xff1a; 选择二叉树中 任意 节点和一个方向&#xff08;左或者右&#xff09;。 如果前进方向为右&#xff0c;那么移动到当前节点的的右子节点&…

盘点AI做自媒体五条赚钱路径,新手小白必看(附教程)

前言 盘点用AI做自媒体赚钱的五条路径&#xff0c;只要学会使用AI工具&#xff0c;你也可以马上赚到钱。 我认为短视频是趋势&#xff0c;但相比于短视频而言&#xff0c;AI则是未来更大的趋势。 AI现在才属于萌芽期&#xff0c;好比是98年的互联网和07年的移动互联网&#x…

Jmeter使用JSON Extractor提取多个变量

1.当正则不好使时&#xff0c;用json extractor 2.提取多个值时&#xff0c;默认值必填&#xff0c;否则读不到变量

Linux - Shell 以及 权限问题

目录 Shell的运行原理 Linux权限问题 Linux权限的概念 如何实现用户账号之间的切换 如何仅提升当前指令的权限 如何将普通用户添加到信任列表 Linux权限管理 文件访问者的分类&#xff08;人&#xff09; 文件类型和访问权限&#xff08;事物属性&#xff09; 文件权限值的表…

【基于深度学习方法的激光雷达点云配准系列之GeoTransformer】——模型部分浅析(1)

【GeoTransformer系列】——模型部分 1. create_model2. model的本质3. 模型的主要结构3.1 backbone3.2 transformer本篇继续对GeoTransformer/experiments/geotransformer.kitti.stage5.gse.k3.max.oacl.stage2.sinkhorn/下面的trainval.py进行详细的解读,主要是模型部分, 可以…

一款针对Webpack等前端打包工具所构造的网站进行快速、高效安全检测的扫描工具

免责声明 由于传播、利用本公众号夜组安全所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;公众号夜组安全及作者不为此承担任何责任&#xff0c;一旦造成后果请自行承担&#xff01;如有侵权烦请告知&#xff0c;我们会立即删除…