C++11线程库 (七) 线程的停止

2023-05-16

在这篇文章里,我们将会讨论如何在C++11下停止和终止一个线程。

为什么C++11不直接提供一个停止线程的方法?这是因为线程在停止之前可能有一些资源需要被释放、关闭,比如说:

  • 线程中已经获取锁的所有权,突然退出谁将会负责这个锁所有权释放?
  • 如果线程打开了一写文件流,谁将会回收这个文件资源?
  • 如果线程中有动态内存分配,内存泄露如何避免?

简单来说就是线程退出时可能有资源需要回收,而C++11无法知晓用户具体使用了哪些资源。虽然如此,但是我们仍可以通过简单的方式实现线程退出:即定时检查或者程序中某个节点检查,检查的内容是是否退出,处理方式是释放所有的资源优雅退出。

一、使用std::future<>停止一个线程

我们可以向线程传递一个std::future<void>对象,为什么是传递一个空值,这是因为我们只是想要信号而不是真正的想要获取值。

首先创建一个值为void的promise对象:

std::promise<void> exitSignal;

现在我们可以在主线程中绑定promise和future:

std::future<void> futureObj=exitSignal.get_future();

然后在主函数中创建线程并往线程传递future对象:

std::thread th(&threadFunction,std::move(futureObj));

在线程的内部,除了完成一些线程工作外,还持续检查线程是否需要退出(在这里的条件就是future对象值是否可用)

void threadFunction(std::future<void> futureObj)
{
    std::cout << "Thread Start" << std::endl;
    while (futureObj.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout)
    {
        std::cout << "Doing Some Work" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
    std::cout << "Thread End" << std::endl;
}

只要我们设置promise对象值,在线程中的promise将会收到信号已经准备就绪的信号:

exiSignal.set_value();

整个例子如下:

#include <thread>
#include <iostream>
#include <assert.h>
#include <chrono>
#include <future>
void threadFunction(std::future<void> futureObj)
{
    std::cout << "Thread Start" << std::endl;
    while (futureObj.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout)
    {
        std::cout << "Doing Some Work" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
    std::cout << "Thread End" << std::endl;
}
int main()
{
    // Create a std::promise object
    std::promise<void> exitSignal;
    //Fetch std::future object associated with promise
    std::future<void> futureObj = exitSignal.get_future();
    // Starting Thread & move the future object in lambda function by reference
    std::thread th(&threadFunction, std::move(futureObj));
    //Wait for 10 sec
    std::this_thread::sleep_for(std::chrono::seconds(10));
    std::cout << "Asking Thread to Stop" << std::endl;
    //Set the value in promise
    exitSignal.set_value();
    //Wait for thread to join
    th.join();
    std::cout << "Exiting Main Function" << std::endl;
    return 0;
}

可以将上述过程理解为一个信号与槽,信号是std::promise,槽是线程函数中等待的信号的futureObj对象。 std::future<void> futureObj = exitSignal.get_future();则是绑定信号与槽的QObject::connected。

二、创建一个可以停止的线程

方法一的缺陷在于,我们需要重复创建promise和future多次,以便实现一个可退出的线程。

我们完全可以借助面向对象的方法来避免这种麻烦的事儿。

/*
 * Class that encapsulates promise and future object and
 * provides API to set exit signal for the thread
 */
class Stoppable
{
    std::promise<void> exitSignal;
    std::future<void> futureObj;
public:
    Stoppable() :
            futureObj(exitSignal.get_future())
    {
    }
    Stoppable(Stoppable && obj) : exitSignal(std::move(obj.exitSignal)), futureObj(std::move(obj.futureObj))
    {
        std::cout << "Move Constructor is called" << std::endl;
    }
    Stoppable & operator=(Stoppable && obj)
    {
        std::cout << "Move Assignment is called" << std::endl;
        exitSignal = std::move(obj.exitSignal);
        futureObj = std::move(obj.futureObj);
        return *this;
    }
    // Task need to provide definition  for this function
    // It will be called by thread function
    virtual void run() = 0;
    // Thread function to be executed by thread
    void operator()()
    {
        run();
    }
    //Checks if thread is requested to stop
    bool stopRequested()
    {
        // checks if value in future object is available
        if (futureObj.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
            return false;
        return true;
    }
    // Request the thread to stop by setting value in promise object
    void stop()
    {
        exitSignal.set_value();
    }
};

这样一来,创建一个可停止的任务只需要继承这个类并重写run方法即可:

/*
 * A Task class that extends the Stoppable Task
 */
class MyTask: public Stoppable
{
public:
    // Function to be executed by thread function
    void run()
    {
        std::cout << "Task Start" << std::endl;
        // Check if thread is requested to stop ?
        while (stopRequested() == false)
        {
            std::cout << "Doing Some Work" << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
        std::cout << "Task End" << std::endl;
    }
};

现在让我们来看看如何使用这个可停止的线程任务:

// Creating our Task
MyTask task;
//Creating a thread to execute our task
std::thread th([&]()
{
    task.run();
});
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "Asking Task to Stop" << std::endl;
// Stop the Task
task.stop();
//Waiting for thread to join
th.join();

整个程序如下:

include <thread>
#include <iostream>
#include <assert.h>
#include <chrono>
#include <future>
/*
 * Class that encapsulates promise and future object and
 * provides API to set exit signal for the thread
 */
class Stoppable
{
    std::promise<void> exitSignal;
    std::future<void> futureObj;
public:
    Stoppable() :
            futureObj(exitSignal.get_future())
    {
    }
    Stoppable(Stoppable && obj) : exitSignal(std::move(obj.exitSignal)), futureObj(std::move(obj.futureObj))
    {
        std::cout << "Move Constructor is called" << std::endl;
    }
    Stoppable & operator=(Stoppable && obj)
    {
        std::cout << "Move Assignment is called" << std::endl;
        exitSignal = std::move(obj.exitSignal);
        futureObj = std::move(obj.futureObj);
        return *this;
    }
    // Task need to provide defination  for this function
    // It will be called by thread function
    virtual void run() = 0;
    // Thread function to be executed by thread
    void operator()()
    {
        run();
    }
    //Checks if thread is requested to stop
    bool stopRequested()
    {
        // checks if value in future object is available
        if (futureObj.wait_for(std::chrono::milliseconds(0)) == std::future_status::timeout)
            return false;
        return true;
    }
    // Request the thread to stop by setting value in promise object
    void stop()
    {
        exitSignal.set_value();
    }
};
/*
 * A Task class that extends the Stoppable Task
 */
class MyTask: public Stoppable
{
public:
    // Function to be executed by thread function
    void run()
    {
        std::cout << "Task Start" << std::endl;
        // Check if thread is requested to stop ?
        while (stopRequested() == false)
        {
            std::cout << "Doing Some Work" << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(1000));
        }
        std::cout << "Task End" << std::endl;
    }
};
int main()
{
    // Creating our Task
    MyTask task;
    //Creating a thread to execute our task
    std::thread th([&]()
    {
        task.run();
    });
    std::this_thread::sleep_for(std::chrono::seconds(10));
    std::cout << "Asking Task to Stop" << std::endl;
    // Stop the Task
    task.stop();
    //Waiting for thread to join
    th.join();
    std::cout << "Thread Joined" << std::endl;
    std::cout << "Exiting Main Function" << std::endl;
    return 0;
}

[1] https://thispointer.com/c11-how-to-stop-or-terminate-a-thread/

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++11线程库 (七) 线程的停止 的相关文章

  • 虚拟网络namespace 到bridge

    前言 容器的网络是一大难点 xff0c 不管是docker 还是kubernetes 都绕不开计算机网络 以下的介绍主要以计算机网络的namespace 和bridge 两个方面来展开介绍 xff0c 方便深入理解容器的网络原理 1 nam
  • 用OpenCV实现目标追踪的八种方法(转)

    原文地址 xff1a http m elecfans com article 722414 html 编者按 xff1a 目标跟踪作为机器学习的一个重要分支 xff0c 加之其在日常生活 军事行动中的广泛应用 xff0c 很多国内外学者都对
  • turbostat超频检测工具

    介绍 turbostat为Intel提供的超频检测工具 xff0c 可以真正在Linux下获取睿频频率的工具 由下可知 xff1a 物理cpu个数为2 xff0c 核心数为14 xff0c 支持超线程 xff0c 逻辑cpu数为56 xff
  • C# HttpClient Digest 摘要认证 Cookie设置

    C HttpClient Digest 摘要认证 Cookie设置 1 创建凭证信息集 2 创建HttpClientHandler 3 创建HttpClient 4 发生请求 span class token comment 创建凭证信息集
  • MongoDB 批量操作(bulkWrite)

    一 概述 mongodb 3 2 版中的新版本提供了db collection bulkWrite 方法提供了执行批量插入 更新和删除操作的能力 mongodb 还支持批量插入 db collection insertMany 1 1 语法
  • Linux动态库的编译与使用(两种方式:链接进可执行程序、动态加载)

    第一步 xff1a 编写Linux程序库 文件1 动态库接口文件 span class token comment 动态库接口文件getmaxlen h span span class token macro property span c
  • ES 索引文档,按_id查找、更新、删除文档

    一 索引 xff08 新建 xff09 文档 通过使用 index API xff0c 文档可以被 索引 存储和使文档可被搜索 但是首先 xff0c 我们要确定文档的位置 正如我们刚刚讨论的 xff0c 一个文档的 index type 和
  • ES排序

    排序 为了按照相关性来排序 xff0c 需要将相关性表示为一个数值 在 Elasticsearch 中 xff0c 相关性得分 由一个浮点数进行表示 xff0c 并在搜索结果中通过 score 参数返回 xff0c 默认排序是 score
  • ES基于completion suggest实现搜索提示

    Term Suggester xff0c 基于编辑距离 xff0c 对analyze过的单个term去提供建议 xff0c 并不会考虑多个term 词组之间的关系 quert gt queryPhrase Suggester xff0c 在
  • 时间间隔宏计算

    对结果按时间间隔分桶 span class token macro property span class token directive keyword define span TIME INTERVAL a b a lt lt 32 4
  • 容器环境下IP跨网闸映射kafka部署

    一 listeners 和 advertised listeners 在公司内网部署 kafka 集群只需要用到 listeners xff0c 内外网需要作区分时才需要用到advertised listeners listeners 学名
  • C/C++ 中头文件相互包含引发的问题(was not declared in this scope)

    问题引入 最近遇到一个问题 xff0c 重构的代码编译报定义的某个宏was not declared in this scope xff0c 但是明明已经引入了包含此宏的头文件 问题分析 转载内容 我把问题脱离于项目简单描述一下 xff1a
  • 字符的全排列、字符的组合

    一 字符的全排列 题目描述 输入一个字符串 按字典序打印出该字符串中字符的所有排列 例如输入字符串abc 则打印出由字符a b c所能排列出来的所有字符串abc acb bac bca cab和cba 输入描述 输入一个字符串 长度不超过9
  • HTTP带用户名和密码请求

    import java io IOException import org apache commons codec binary Base64 import com cn mid system urls UrlUtils import o
  • n*m的格子中正方形个数和长方形个数

    问题描述 1 xff0e 设有一个nm方格的棋盘 xff08 1 m n 100 xff09 求出该棋盘中包含多少个正方形 多少个长方形 xff08 不包括正方形 xff09 例如 xff1a 当n 61 2 xff0c m 61 3时 正
  • Linux数字权限

    linux系统文件夹 从左至右 xff0c 第一位数字代表文件所有者的权限 xff1b 第二位数字代表同组用户的权限 xff1b 第三位数字代表其他用户的权限 而具体的权限是由数字来表示的 xff1a 读取的权限等于4 xff0c 用r表示
  • 八大排序算法C语言实现

    1 插入排序 1 1 直接插入排序 基本原理 xff1a 将第n个数插入已经排序好的 xff0c 长度为n 1的序列中 从n 1长度的序列中查找出待插入的元素应该插入的位置 xff1b 给插入元素腾出空间 操作方法 xff1a 从第2个数开
  • CLion下的gtest测试

    在mac环境中 xff0c 使用CLion编译的简单gtest程序 一 下载gtest源码 加入工程中 xff1a 二 编写CMakeList txt 文件 在文件中添加头文件和链接库文件 xff0c 并将链接库文件与目标文件进行链接 sp
  • python中json与dict的互相转换(编码与解码)及其简单实现

    在json模块中 将json转换为dict数据的方法有 xff1a load loads xff08 xff09 其中 xff0c load 方法从文件中提取数据进行转换 将dict转换为json数据的方法有 xff1a dump dump
  • gtest中ASSERT与EXPECT断言的区别

    参考资料查找到ASSERT断言与EXPECT断言的区别 xff1a ASSERT 系列的断言 xff0c 当检查点失败时 xff0c 退出当前函数 xff08 注意 xff1a 并非退出当前案例 xff09 EXPECT 系列的断言 xff

随机推荐

  • gtest参数化

    步骤 xff1a 1 创建一个类 xff0c 继承testing TestWithParam xff0c T是你需要参数化的参数类型 xff0c 比如参数类型为int 2 使用新宏TEST P替代TEST 在TEST P宏里 xff0c 可
  • gtest中字符串比较是否相等

    1 EXPECT EQ val1 xff0c val2 xff09 class StringCmpTest span class token punctuation span public testing span class token
  • 白盒测试——逻辑覆盖

    白盒测试中的逻辑覆盖有以下六种方法 xff1a 1 语句覆盖 xff1a 每个可执行语句至少被执行一次 2 判定覆盖 xff1a 每个判定的每个分支都至少执行一次 3 条件覆盖 xff1a 判定式中每个条件的每个分支至少执行一次 4 判定条
  • 计算机网络面试题整理

    面试很多时候被问到的问题 xff0c 感觉都没答好 xff0c 统一整理一下 更新 8 23 GET和POST的区别 xff1f GET和POST方法没有实质上区别 xff0c 只是报文格式不同 GET和POST是HTTP协议中的两种请求方
  • 【C语言】c/c++中常用的预定义宏:__LINE__, __func__, __FILE__, __DATE__, __TIME__

    ANSI C标准中的预定义宏 xff08 也是常用的 xff09 xff1a LINE xff1a 在代码中插入当前行号 func xff1a 在代码中插入当前行所在的函数的函数名 FILE xff1a 在代码中插入当前文件的文件名 DAT
  • 无人机入门知识

    无人机入门知识 无人机的定义 现在提到的 无人机 xff0c 通常是说 无人飞行载具 xff08 Unmanned Aerial Vehicle xff0c 简称UAV xff09 xff0c 或称无人飞机 无人飞机系统 xff08 Unm
  • http协议

    一 认识url url被称为统一资源定位符 xff0c 用来表示从互联网上得到的资源位置和访问这些资源的方法 他的表示方法一般为 xff1a span class token operator lt span 协议 span class t
  • 大小端区别和判断

    在代码中看到往寄存器写数据的时候 xff0c 使用到 volatile uint32 t address 61 cpu to le32 value xff0c 进一步追踪 xff0c if BYTE ORDER 61 61 LITTLE E
  • 结构体对齐详解

    1 结构体数据成员对齐的意义 许多实际的计算机系统对基本类型数据在内存中存放的位置有限制 xff0c 它们会要求这些数据的起始地址的值是某个数k的倍数 xff0c 这就是所谓的内存对齐 xff0c 而这个k则被称为该数据类型的对齐模数 al
  • C语言基础之格式化占位符

    在 printf 系列函数中 xff0c 下列哪个格式化占位符有可能导致内存任意写风险 A A n B d C p D s 使用printf修改变量的值 VS2008中使用 n输出遇到的问题及解决方法 include lt stdio h
  • 不常用的访问控制方式——http auth

    今天遇到一个http auth方式的访问控制 xff0c 访问页面时出现输入用户名与密码的验证 xff1a 使用ncrack爆破登陆后 xff0c 当想要爆破目录时 xff0c 必须每次请求都带着authorication头 xff0c 使
  • 数据库中字符串匹配函数like、rlike、instr、regexp_extract

    instr 简介 MySQL hive中函数 xff0c instr str substr position occurrence 其中str代表从哪个字符串中搜索 xff0c substr代表搜索哪个子字符串 xff0c 返回值为子字符串
  • 【C语言】实现linux下的基于C语言的一个简单的TCP客/服 端的通信

    对于基础的好的朋友可以直接取代码 xff0c 如果想要看详细解析的朋友可以详看下方的解析 TCP服务端代码 xff1a include lt stdio h gt include lt string h gt include lt stdl
  • 实验二 OpenGL的简单动画

    ZZU的学弟学妹们不要抄作业哦 一 实验目的 1 掌握OpenGL的闲置函数 2 掌握OpenGL的时间函数 3 掌握OpenGL的简单动画功能 4 了解OpengGL裁剪窗口 视区 显示窗口的概念和它们之间的关系 5 进一步掌握OpenG
  • 使用脚本(命令行)编译KEIL工程

    参考KEIL官网 http www keil com support man docs uv4 uv4 commandline htm Keil Build bat脚本 64 echo off set UV 61 D Keil v5 UV4
  • (二)五次多项式轨迹规划

    一 三次多项式轨迹规划的缺陷 上一篇文章说道 xff0c 三次多项式轨迹规划只能够保证速度和位移连续 xff0c 并不能保证加速度连续 加速度不连续将会对使电机抖动 甚至冲击 二 轨迹规划中的五次多项式 我们对加速度数值进行指定 xff0c
  • (三)抛物线过渡的线性函数规划

    前面说到 xff0c 无论是三次还是五次多项式进行规划存在以下缺点 xff1a 位移往返没有匀速段 这一节中 xff0c 我们的研究对象是初速度和末速度都为0关节运动 一 无过渡线性函数 假设时刻 t t t 和角度
  • GCC的学习(二)头文件及其库制作

    当前目录 43 相对路径 61 绝对路径大写i xff0c I头文件包含路径大写l xff0c L库文件路径小写l xff0c l库名字 xff08 去头去尾没有lib xff0c 也没有so ldd 查看动态库连接库存在性及其路径nm 查
  • VSCODE(八)launch 调试与运行

    前面讲了如何配置任务文件tasks json xff0c 调试功能在程序程序经常会用上 xff0c 那么VSCODE是完成调试功能的呢 xff1f 答 xff1a vscode文件夹内的launch json文件配置 xff0c 一些调试器
  • C++11线程库 (七) 线程的停止

    在这篇文章里 xff0c 我们将会讨论如何在C 43 43 11下停止和终止一个线程 为什么C 43 43 11不直接提供一个停止线程的方法 xff1f 这是因为线程在停止之前可能有一些资源需要被释放 关闭 xff0c 比如说 xff1a