C++并发:线程管控

1 线程基本管控

每个C++程序都含有至少一个线程,即运行main()的线程,它由C++运行时系统启动。随后程序可以发起更多线程,它们以别的函数作为入口。这些新线程连同起始线程并发运行。当main()返回时,程序就会退出;同样,当入口函数返回时,对应的线程随之终结。如果借std::thread对象管控线程,即可选择等他结束。

1.1 发起线程

线程通过构造std::thread对象而启动,该对象指明线程要运行的任务。

void do_some_work();
std::thread myThread(do_some_work);

任何可调用类型都适用于std::thread。所以,作为代替,可以设计一个带有函数调用操作符的类(应当是下面的operator)

class background_task
{
public:void operator() () const{do_something();do_something_else();}
};background_task f;
std::thread my_thread(f);

f被复制到属于新线程的存储空间中,在那里被调用,由新线程执行。

1.1.1 与函数声明进行区分

如果传入std::thread的是临时变量,不是具名变量,那么调用构造函数的语法有可能与函数声明相同。这种情况,编译器会将其解释成函数声明。

声明为函数:函数名为my_thread,只接收一个参数,返回std::thread对象
std::thread my_thread(background_task());可以通过多用一对圆括号或使用新式的统一初始化语法
std::thread my_thread((background_task()));
std::thread my_thread{background_task()};还可以使用lambda表达式
std::thread my_thread([]{do_something();do_something_else();
});

1.2 汇合与分离

在启动线程后,需要明确是要等待他结束(也就是汇合)还是任由他独立运行(也就是分离)。如果等到std::thread销毁的时候还没有决定好,那么std::thread的析构函数将调用std::terminate()终止整个程序。

如果选择了分离,且分离时新线程还未运行结束,那将继续运行,甚至在std::thread对象销毁很久之后依然运行,它只有最终从线程函数返回时才会结束运行。

假设程序不等待线程结束,那么在线程运行结束前,我们要保证它所访问的外部数据始终正确,有效。由于使用多线程,所以我们可能会经常面临对象生存期的问题。比如下面的案例:

struct func
{int& i;func(int& i_):i(i_){}void operator() (){for (unsigned j=0; j<1000000; ++j) {do_something(i);    隐患:可能访问悬空引用}}
};void oops()
{int some_local_state=0;func my_func(some_local_state);std::thread my_thread(my_func);my_thread.detach();        不等待新线程结束新线程可能仍运行,而主线程的函数却已经结束
}

主线程新线程
构造my_func对象,引用局部变量some_local_state
通过my_thread对象启动新线程
新线程启动
调用func::operator()
分离新线程my_thread

运行func::operator();

调用do_something()函数,

进而引用局部变量some_local_state

销毁局部变量some_local_state

仍在运行

退出oops()

继续运行func::operator();

调用do_something()函数,

进而引用some_local_state,

导致未定义行为

因此:以下做法不可取:意图在函数中创建线程,并让线程访问函数的局部变量。除非线程肯定会在该函数退出前结束。或者是汇合新线程,此举可以保证在主线程的函数退出前,新线程执行完毕。

1.2.1 join—等待线程完成

若需等待线程完成,那么可以在与之关联的std::thread实例上,通过调用成员函数join()实现。对于上面的代码,就是把detach换成join。就能够保证在oops退出前,新线程结束。

对于一个线程,join仅能被调用一次,被调用后线程不再可汇合,成员函数joinable将返回false。

要注意,如果线程启动后有异常抛出,而join尚未执行,该join调用会被略过

使用thread_guard保证在抛出异常时,退出路径的先后顺序与不抛出异常时一致。

也就是在析构函数中调用join

class thread_guard {std::thread& t;
public:explicit thread_guard(std::thread& t_) : t(t_){}~thread_guard() {if (t.joinable()) {t.join();}}thread_guard(thread_guard const&)=delete;thread_guard& operator=(thread_guard const&)=delete;
};struct func {int& i;explicit func(int& i_) : i(i_) {};void operator() () {for (unsigned j = 0; j < 1000000; ++j) {do_somthing();}}
};void f() {int some_local_state=0;func my_func(some_local_state);std::thread t(my_func);thread_guard g(t);do_something_in_current_thread();
}

1.2.2 detach—在后台运行线程

会令线程在后台运行,因此与之无法直接通信。其归属权和控制权都交给了C++运行时库,由此保证,一旦线程退出,与之关联的资源都会被正确回收。

只有在joinable返回true时,才能调用detach。

2 向线程函数传递参数

直接向std::thread的构造函数增添更多参数即可。需要注意的是,线程具有内部存储空间,参数会按照默认方式先复制到该处,新创建的执行线程才能直接访问它们。然后,这些副本被当成临时变量,以右值的形式传给新线程上的函数或可调用对象。即便函数相关参数按设想应该是引用,上述过程依然会发生。

void f(int i, std::string const& s);
std::thread t(f, 3, "hello");void f(int i, std::string const& s);
void oops(int some_param)
{char buffer[1024];sprintf(buffer, "%i", some_param);std::thread t(f, 3, buffer);// std::thread t(f, 3, std::string(buffer));t.detach();
}

但是上述例子将字符串的引用复制到了thread的存储空间,当调用thread的外层函数销毁时,buffer将不存在,无法访问这个引用。可以使用注释里的方法,先转换成std::string对象(buffer相当于一个指针)

void update_data_for_widget(widget_id w, widget_data& data);
void oops_again(widget_id w)
{widget_data data;std::thread t(update_data_for_widget, w, data);display_status();t.join();process_widget_data(data);
}

根据update_data_for_widget函数的声明,第二个参数会以引用的方式传入update_data_for_widget,但是std::thread的构造函数并不知情,会直接复制提供的值。随后线程库内部会把参数副本当成move-only(只移型别),以右值的形式传递。最终,update_data_for_widget会收到右值,因为update_data_for_widget预期接受非const引用,我们不能向他传递右值

解决方法是,按照如下方式改写(std::ref

std::thread t(update_data_for_widget, w, std::ref(data));

这样就保证了传入update_data_for_widget函数的不是变量data的临时副本,而是指向变量data的引用,因此能够编译成功。

2.2 调用对象的方法

若要调用一个对象对应的方法,则需要传递方法地址和对象地址,第三个参数作为该方法的第一个入参。

class X {
public:void do_lengthy_work();};
X my_x;
std::thread t(&X::do_lengthy_work, &my_x);

上述代码调用对象my_x的do_lengthy_work方法。

2.3 只能移动的方式传递参数

3 移交线程归属权

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

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

相关文章

网络爬虫性能提升:requests.Session的会话持久化策略

网络爬虫面临的挑战 网络爬虫在运行过程中可能会遇到多种问题&#xff0c;包括但不限于&#xff1a; IP被封禁&#xff1a;频繁的请求可能会被网站的反爬虫机制识别&#xff0c;导致IP被封。请求效率低&#xff1a;每次请求都需要重新建立TCP连接&#xff0c;导致请求效率低下…

基于华为atlas的车辆车型车牌检测识别

整体分为2个部分&#xff0c;也就是2个模型&#xff0c;车辆检测、车型检测、车牌检测这3个功能是一个基于yolov5的模型实现&#xff0c;车牌识别是基于PaddleOCR中的PP-OCRv3的模型实现。 车辆检测数据集制作&#xff1a; 车辆检测、车型检测、车牌检测的数据集主要从coco数…

打破视障壁垒,百度文心快码无障碍版本助力视障IT从业者就业无“碍”

有AI无碍 钟科&#xff1a;被黑暗卡住的开发梦 提起视障群体的就业&#xff0c;绝大部分人可能只能想到盲人按摩。但你知道吗&#xff1f;视障人士也能写代码。 钟科&#xff0c;一个曾经“被黑暗困住”的人&#xff0c;他的世界&#xff0c;因为一场突如其来的疾病&#xff0c…

pytorch torch.utils.checkpoint模块介绍

torch.utils.checkpoint 是 PyTorch 中用于实现 梯度检查点(gradient checkpointing)的模块。它通过在反向传播中 重新计算 前向传播的某些部分,以显著减少激活值的显存占用。 梯度检查点的核心原理 在前向传播中,不是保存每一层的激活值,而是保存输入和部分中间结果。在…

Spring-AI讲解

Spring-AI langchain(python) langchain4j 官网&#xff1a; https://spring.io/projects/spring-ai#learn 整合chatgpt 前置准备 open-ai-key: https://api.xty.app/register?affPuZD https://xiaoai.plus/ https://eylink.cn/ 或者淘宝搜&#xff1a; open ai key魔法…

Python-网络爬虫

随着网络的迅速发展&#xff0c;如何有效地提取并利用信息已经成为一个巨大的挑战。为了更高效地获取指定信息&#xff0c;需定向抓取并分析网页资源&#xff0c;从而促进了网络爬虫的发展。本章将介绍使用Python编写网络爬虫的方法。 学习目标&#xff1a; 理解网络爬虫的基本…

Kafka 性能提升秘籍:涵盖配置、迁移与深度巡检的综合方案

文章目录 1.1.网络和io操作线程配置优化1.2.log数据文件刷盘策略1.3.日志保留策略配置1.4.replica复制配置1.5.配置jmx服务1.6.系统I/O参数优化1.6.1.网络性能优化1.6.2.常见痛点以及优化方案1.6.4.优化参数 1.7.版本升级1.8.数据迁移1.8.1.同集群broker之间迁移1.8.2.跨集群迁…

UniApp 组件的深度运用

一、引言 在当今的移动应用开发领域&#xff0c;跨平台开发已成为主流趋势&#xff0c;而 UniApp 作为其中的佼佼者&#xff0c;备受开发者青睐。UniApp 的强大之处很大程度上源于其丰富且功能多样的组件体系&#xff0c;这些组件宛如精巧的积木&#xff0c;能够帮助开发者快速…

Spring Boot 中 Map 的最佳实践

在Spring Boot中使用Map时&#xff0c;请遵循以下最佳实践: 1.避免在Controller中 直接使用Map。应该使用RequestBody 接收-个DTO对象或者 RequestParam接收参数&#xff0c;然后在Service中处 理Map。 2.避免在Service中 直接使用原始的Map。应该使用Autowired 注入-个专门…

Android ActionBar 技术深度解析

Android ActionBar 技术深度解析 概述 ActionBar 是 Android 应用中的一个核心 UI 组件,用于提供导航、操作和品牌展示。它通常位于应用窗口的顶部,包含应用的标题、导航按钮、操作项等。ActionBar 自 Android 3.0(API 11)引入,并在 Android 5.0(API 21)后被 Toolbar …

【Qt】多元素控件:QListWidget、QTableWidget、QTreeWidget

目录 QListWidget 核心属性&#xff1a; 核心方法&#xff1a; 核心信号&#xff1a; 例子&#xff1a; QListWidgetItem QTableWidget 核心方法&#xff1a; 核心信号 QTableWidgetItem 例子&#xff1a; QTreeWidget 核心方法&#xff1a; 核心信号&#xff1a…

WebSocket 的封装使用

import { ElMessage } from "element-plus";// 全局WebSocket实例 let ws null; let isConnected false; let currentWsUrl ; // 用于存储当前的wsUrl let baseURL ws://XXX.com:8081;const initWebSocket (wsUrl, sendData) > {return new Prom…

119.【C语言】数据结构之快速排序(调用库函数)

目录 1.C语言快速排序的库函数 1.使用qsort函数前先包含头文件 2.qsort的四个参数 3.qsort函数使用 对int类型的数据排序 运行结果 对char类型的数据排序 运行结果 对浮点型数据排序 运行结果 2.题外话:函数名的本质 1.C语言快速排序的库函数 cplusplus网的介绍 ht…

vulnhub靶机billu_b0x精讲

靶机下载 https://www.vulnhub.com/entry/billu-b0x,188/ 信息收集 扫描存活主机 nmap -sP 192.168.73.0/24 192.168.73.141为目标主机&#xff0c;对其进行进一步信息收集 端口扫描 nmap --min-rate10000 -p- 192.168.73.141 目标只开放了22和80端口 针对端口进行TCP探…

react相关报错--持续更新中

日常项目报错记录 一、开源项目问题集合安装依赖和启动问题1. 启动时候报The react-scripts package provided by Create React App requires a dependency: "babel-loader": "8.1.0"问题解决方法1:解决方法2: 二、reactAnt Design使用遇到的问题1. 使用ta…

vue实现下拉多选、可搜索、全选功能

最后的效果就是树形的下拉多选&#xff0c;可选择任意一级选项&#xff0c;下拉框中有一个按钮可以实现全选&#xff0c;也支持搜索功能。 在mounted生命周期里面获取全部部门的数据&#xff0c;handleTree是讲接口返回的数据整理成树形结构&#xff0c;可以自行解决 <div c…

【SpringMVC】REST 风格

REST&#xff08;Representational State Transfer&#xff0c;表现形式状态转换&#xff09;是一种访问网络资源的格式。传统的资源描述方式通常如下&#xff1a; http://localhost/user/getById?id1http://localhost/user/saveUser 而 REST 风格的描述则更简洁&#xff1a…

Jenkins使用记录

参考资料 https://www.cnblogs.com/hanmk/p/6808932.html https://www.cnblogs.com/gltou/p/15329634.html 1、安装jenkisn、 2、安装jenkisn需要的插件 如 git等、 3、java服务引用 jenkins API三方包 4、Jekins启动&#xff0c;手动在jekins控制面板 创建一个 jenkisn 构建模…

Unity Dots理论学习-1.关于性能

如果你是一个有经验的游戏开发者&#xff0c;那么你知道在目标平台上进行性能优化是贯穿整个开发周期的任务。也许你的游戏在高端PC上运行得很好&#xff0c;但在低端移动平台上&#xff0c;帧率是不是会慢很多&#xff0c;导致明显的卡顿&#xff1f;加载时间是不是过长&#…

数据库概念(MySQL第一期)

p.s.这是萌新自己自学总结的笔记&#xff0c;如果想学习得更透彻的话还是请去看大佬的讲解 目录 数据库就是管理数据的仓库 数据库&#xff1a;DataBase(DB)&#xff0c;是存储数据的仓库&#xff0c;数据是有组织的进行存储 数据库管理系统&#xff1a;DataBase Management S…