2.3转移线程的所有权

转移线程的所有权

假设你想要编写一个函数,它创建一个在后台运行的线程,但是向调用函数回传新线程的所有权,而非等待其完成,又或者你想要反过来做,创建一个线程,并将所有权传递给要等待它完成的函数。在任意一种情况下,你都需要将所有权从一个地方转移到另一个地方。

这里就是std::thread支持移动的由来。正如在上一节所描述的,在C++标准库里许多拥有资源的类型,如std::ifstream 和 std::unique_ptr是可移动的(movable),而非可复制的(copyable),并且std::thread就是其中之一。这意味着一个特定执行线程的所有权可以在std::thread实例之间移动,如同接下来的例子。该示例展示了创建两个执行线程,以及在三个std::thread实例t1、t2和t3之间对那些线程的所有权进行转移。

void some_function();
void some_other_function();
std::thread t1(some_function); //❶
std::thread t2 = std::move(t1); //❷
t1 = std::thread(some_other_function); //❸
std::thread t3; //❹
t3 = std::move(t2); //❺
t1 = std::move(t3); //❻ 此赋值将终结程序

首先,启动一个新线程❶并与t1相关联。然后当t2构建完成时所有权被转移给t2,通过调用std::move()来显式地转移所有权❷。此刻,t1不再拥有相关联的执行线程,运行some_function的线程现在与t2相关联。

然后,启动一个新的线程并与一个临时的std::thread对象相关联❸。接下来将所有权转移到t1中,是不需要调用std::move()来显式移动所有权的,因为此处所有者是一个临时对象——从临时对象中进行移动是自动和隐式的。

t3是默认构造的❹,这意味着它的创建没有任何相关联的执行线程。当前与t2相关联的线程的所有权转移到t3❺,再次通过显式调用std::move(),因为t2是一个命名对象。在所有这些移动之后,t1与运行some_other_function 的线程相关联,t2没有相关联的线程,t3与运行some_function的线程相关联。

最后一次移动❻将运行 some_function的线程的所有权转回给t1。但是在这种情况下t1已经有了一个相关联的线程(运行着some_other_function),所以会调用std::terminate()来终止程序。这样做是为了与std::thread的析构函数保持一致。你必须在析构前显式地等待线程完成或是分离,这同样适用于赋值:你不能仅仅通过向管理一个线程的std::thread对象赋值一个新的值来“舍弃”一个线程。

std::thread支持移动意味着所有权可以很容易地从一个函数中被转移出,如清单2.5所示。

//清单2.5 从函数中返回std::thread
#include <thread>void some_function()
{}void some_other_function(int)
{}std::thread f()
{void some_function();return std::thread(some_function);
}
std::thread g()
{void some_other_function(int);std::thread t(some_other_function, 42);return t;
}int main()
{std::thread t1 = f();t1.join();std::thread t2 = g();t2.join();
}

同样地,如果要把所有权转移到函数中,它只能以值的形式接受std::thread的实例作为其中一个参数,如下所示。

void f(std::thread t);void g()
{void some_other_function();f(std::thread(some_function));std::thread t(some_function);f(std::move(t));
}

std::thread支持移动的好处之一,就是你可以建立在清单2.3中thread_guard 类的基础上,同时使它实际上获得线程的所有权。这可以避免thread_guard 对象在引用它的线程结束后继续存在所造成的不良影响,同时也意味着一旦所有权转移到了该对象,那么其他对象都不可以结合或分离该线程。因为这主要是为了确保在退出一个作用城之前线程都已完成,我把这个类称为scoped_thread。其实现如清单2.6所示,同时附带一个简单的示例。

#include <thread>
#include <utility>class scoped_thread
{std::thread t;
public:explicit scoped_thread(std::thread t_): //❶t(std::move(t_)){if(!t.joinable()) //❷throw std::logic_error("No thread");}~scoped_thread(){t.join(); //❸}scoped_thread(scoped_thread const&)=delete;scoped_thread& operator=(scoped_thread const&)=delete;
};void do_something(int& i)
{++i;
}struct func
{int& i;func(int& i_):i(i_){}void operator()(){for(unsigned j=0;j<1000000;++j){do_something(i);}}
};void do_something_in_current_thread()
{}void f()
{int some_local_state;scoped_thread t(std::thread(func(some_local_state))); //❹do_something_in_current_thread();
} //❺int main()
{f();
}

这个例子与清单2.3类似,但是新线程被直接传递到scoped_thread❹,而不是为它创建一个单独的命名变量。当初始线程到达 f❺ 的结尾时,scoped_thread对象被销毁,然后结合❸提供给构造函数❶的线程。使用清单2.3中的thread_guard类,析构函数必须检查线程是不是仍然可结合,你可以在构造函数中❷来做,如果不是则引发异常。

std::thread对移动的支持同样考虑了std::thread对象的容器,如果那些容器是移动感知的(如更新后的std::vector<>)。这意味着你可以编写像清单2.7中的代码,生成一批线程,然后等待它们完成。

#include <vector>
#include <thread>
#include <algorithm>
#include <functional>void do_work(unsigned id)
{}void f()
{std::vector<std::thread> threads;for(unsigned i=0; i<20; ++i){threads.push_back(std::thread(do_work, i)); //生成线程}std::for_each(threads.begin(), threads.end(),std::mem_fn(&std::thread::join)); //轮流在每个线程上调用join()
}int main()
{f();
}

如果线程是被用来细分某种算法的工作,这往往正是所需的。在返回调用者之前,所有线程必须全都完成。当然,清单2.7的简单结构意味着由线程所做的工作是自包含的,同时它们操作的结果纯粹是共享数据的副作用。如果f()向调用者返回一个依赖于这些线程的操作结果的值,那么正如所写的这样,该返回值就得通过检查线程终止后的共享数据来决定。

将std::thread对象放到std::vector中是线程迈向自动管理的一步。与其为那些线程创建独立的变量并直接与之结合,不如将它们视为群组。你可以进一步创建在运行时确定的动态数量的线程,更进一步地利用这一步,而不是如清单2.7中的那样创建固定的数量。

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

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

相关文章

一个 git 仓库下拥有多个项目的 git hooks 配置方案

前言 通常情况下&#xff0c;一个 git 仓库就是一个项目&#xff0c;只需要配置一套 git hooks 脚本就可以执行各种校验任务。对于 monorepo 项目也是如此&#xff0c;monorepo 项目下的多个 packages 之间&#xff0c;它们是有关联的&#xff0c;可以互相引用&#xff0c;所以…

CRM系统如何进行公海池线索分配自动化?

在销售过程中&#xff0c;线索分配是一个非常重要的环节。传统的线索分配方式往往是由销售主管手动进行&#xff0c;不仅效率低下&#xff0c;还存在着不公平、不灵活的问题。因此&#xff0c;许多企业通过CRM来实现公海池线索分配自动化。 1、基于规则的分配 CRM可以让用户设…

C语言易错知识点总结2

函数 第 1 题&#xff08;单选题&#xff09; 题目名称&#xff1a; 能把函数处理结果的二个数据返回给主调函数&#xff0c;在下面的方法中不正确的是&#xff1a;&#xff08; &#xff09; 题目内容&#xff1a; A .return 这二个数 B .形参用数组 C .形参用二个指针 D .用…

express学习笔记6 - 用户模块

新建router/user.js const express require(express) const routerexpress.Router() router.get(/login, function(req, res, next) {console.log(/user/login, req.body)res.json({code: 0,msg: 登录成功})})module.exportsrouter 在router/user.js引入并使用 const us…

Vue进阶(幺叁陆): transition标签实现页面跳转动画

文章目录 一、前言二、方案实现三、延伸阅读 transition标签四、拓展阅读 一、前言 在Vue项目开发过程中&#xff0c;应用全家桶vue-router实现路由跳转&#xff0c;且页面前进、后退跳转过程中&#xff0c;分别对应不同的切换动画。vue-router 切换页面时怎么设置过渡动画&am…

ansible-kubeadm在线安装单masterk8s v1.19-v1.20版本

ansible可以安装的KS8版本如下&#xff1a; 请按照此博客中的内容操作后&#xff0c;才可以通过下面的命令查询到版本。 [rootk8s-master01 ~]# yum list kubectl --showduplicates | sort -r kubectl.x86_64 1.20.0-0 kubern…

MySQL日志——错误日志、二进制日志

错误日志二进制日志查询日志慢查询日志 1.错误日志 查看日志位置&#xff1a; show variables like %log_error%查看错误日志&#xff1a; tail -f /var/log/mysql.log2.二进制日志 show variables like %log_bin%;cd /var/lib/mysql ll2.1 日志格式 查看日志格式指令&…

计算机视觉与图形学-神经渲染专题-ConsistentNeRF

摘要 Neural Radiance Fields (NeRF) 已通过密集视图图像展示了卓越的 3D 重建能力。然而&#xff0c;在稀疏视图设置下&#xff0c;其性能显着恶化。我们观察到&#xff0c;在这种情况下&#xff0c;学习不同视图之间像素的 3D 一致性对于提高重建质量至关重要。在本文中&…

Nvidia Triton 使用入门教程

1 相关预备知识 模型&#xff1a;包含了大量参数的一个网络&#xff08;参数结构&#xff09;&#xff0c;体积10MB-10GB不等。模型格式&#xff1a;相同的模型可以有不同的存储格式&#xff08;可类比音视频文件&#xff09;&#xff0c;目前主流有torch、tf、onnx和trt&…

vscode 无法导入自己写的模块文件(.py)问题

问题主要是在 vscode中 python 的读入模块路径存在问题&#xff0c;下面先介绍下python的模块读入路径&#xff1a; 什么是PYTHONPATH? PYTHONPATH是一个环境变量&#xff0c;用于指定Python解释器在导入模块时搜索模块的路径。当我们导入一个模块时&#xff0c;Python解释器…

Leetcode 268. Missing Number

Problem Given an array nums containing n distinct numbers in the range [0, n], return the only number in the range that is missing from the array. Algorithm Sum all the numbers as x x x and use n ( n 1 ) 2 − x \frac{n(n1)}{2} - x 2n(n1)​−x. Code …

k8s概念-secret

回到目录 k8s secrets用于存储和管理一些敏感数据&#xff0c;比如密码&#xff0c;token&#xff0c;密钥等敏感信息。它把 Pod 想要访问的加密数据存放到 Etcd 中。然后用户就可以通过在 Pod 的容器里挂载 Volume 的方式或者环境变量的方式访问到这些 Secret 里保存的信息了…

npm,yarn,pnpm

原理 npm、yarn和pnpm都是用于管理Node.js项目依赖的包管理工具&#xff0c;下面对它们进行详细讲解&#xff1a; npm&#xff08;Node Package Manager&#xff09;&#xff1a; npm是Node.js的默认包管理工具&#xff0c;也是最早被广泛使用的。npm使用package.json文件来管…

【多模态】21、BARON | 通过引入大量 regions 来提升模型开放词汇目标检测能力(CVPR2021)

文章目录 一、背景二、方法2.1 主要过程2.2 Forming Bag of Regions2.3 Representing Bag of Regions2.4 Aligning bag of regions 三、效果 论文&#xff1a;Aligning Bag of Regions for Open-Vocabulary Object Detection 代码&#xff1a;https://github.com/wusize/ovdet…

pytorch(续周报(1))

文章目录 2.1 张量2.1.1 简介2.1.2 创建tensor2.1.3 张量的操作2.1.4 广播机制 2.2 自动求导Autograd简介2.2.1 梯度 2.3 并行计算简介2.3.1 为什么要做并行计算2.3.2 为什么需要CUDA2.3.3 常见的并行的方法&#xff1a;网络结构分布到不同的设备中(Network partitioning)同一层…

微服务系列<3>---微服务的调用组件 rpc 远程调用

什么是rpc调用,让我们调用远程方法就像调用本地方法一样 这就属于rpc调用 rpc是针对于本地来说的 调用远程方法根调用本地方法一样 如果能达到这种效果 就是rpc调用如果达到一种效果 调用远程和调用本地一样 他就是一种rpc框架2个微服务 之间发的调用 我们之前通过ribbon的方式…

springboot访问请求404的原因

是记录&#xff0c;可能出现错误 可能出现的原因 1.你请求的URL路径不对,比如说你请求的路径是/usr/list,GET方法,但是你UserController上面的RequestMapping是这个样子:RequestMapping(“user”)&#xff0c;有可能哈 2.前端的请求时GET方法&#xff0c;后端对应的处理函数的方…

【Linux命令200例】whereis用于搜索以及定位二进制文件

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;全栈领域新星创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;本文已收录于专栏&#xff1a;Linux命令大全。 &#x1f3c6;本专栏我们会通过具体的系统的命令讲解加上鲜…

IDA+Frida分析CTF样本和Frid源码和objection模块

文章目录 一些资料IDA调试命令IDA调试安卓的10个技巧objection基本使用 Wallbreaker1frida源码阅读之frida-java 第一个实例EasyJNI第二个实例objection资料 art_trace2.pyart_trace2.js IDAFrida分析CTF样本和Frid源码和objection模块 一些资料 IDA调试命令 adb devices adb…

C#设计模式之---抽象工厂模式

抽象工厂模式&#xff08;Abstract Factory&#xff09; 抽象工厂模式&#xff08;Abstract Factory Pattern&#xff09;是一个超级工厂创建其他工厂。该超级工厂又称为其他工厂的工厂。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。工厂方…