当前位置: 首页 > article >正文

C++标准线程库实现优雅退出的方式

目录

1.通过设置共享退出标记

2.使用std::jthread创建线程

3.定义消息类型的方式

4.注意事项


1.通过设置共享退出标记

        定义一个退出变量bool stop = false; 表示线程是否应该停止。在主线程中设置标记stop=true,然后join一直等待,然后线程循环检测到stop是否为true,为true则表示线程该退出了。示例代码如下:

IThread.h

#ifndef  _I_THREAD_H_
#define  _I_THREAD_H_
#include "LinkGlobal.h"
#include <Thread>
#include <memory>

LINK_CORE_NAMESPACE_BEGIN

class IThread
{
public:
	explicit IThread();
	virtual ~IThread() = default;

public:
	void start();
	void join();

protected:
	virtual void run() = 0;

private:
	std::unique_ptr<std::thread> m_thread;
	bool m_bStarted;
};

LINK_CORE_NAMESPACE_END

#endif

IThread.cpp

#include "IThread.h"

LINK_CORE_NAMESPACE_BEGIN

IThread::IThread()
	:m_bStarted(false)
	, m_thread(nullptr)
{

}

void IThread::start()
{
	if (m_bStarted) {
		join();
		return;
	}

	m_thread.reset(new std::thread(&IThread::run, this));
	m_bStarted = true;
}

void IThread::join()
{
	if (m_bStarted && m_thread) {
		if (m_thread->joinable()) {
			m_thread->join();
		}
	}
	m_bStarted = false;
}

LINK_CORE_NAMESPACE_END

QueryDataCmdThread.h

#pragma once
#include "IThread.h"
#include "DataType.h"
#include <string>
#include <QByteArray>
using namespace xyLinkCore;

class CQueryDataCmdThread : public IThread
{
public:
	CQueryDataCmdThread (PUInt64 chaissID, PUInt64 signalID);
	virtual ~CQueryDataCmdThread ();

public:
	int start();
	int stop();

private:
	void  run() override;
	void  sendQueryCmd();
	void  encodeData();

private:
	bool   m_bStop;
	PUInt64 m_chaissID;
	PUInt64 m_signalID;
	bool   m_status;
	std::string m_waveName;
	QByteArray  m_data;
};

QueryDataCmdThread.cpp

#include "QueryDataCmdThread.h"
#include "HardwareDataProcCenter.h"

CQueryDataCmdThread::CQueryDataCmdThread(PUInt64 chaissID, PUInt64 signalID)
	: m_bStop(false)
	, m_chaissID(chaissID)
	, m_signalID(signalID)
	, m_status(false)
{
	encodeData();
}

CQueryDataCmdThread::~CQueryDataCmdThread()
{
	stop();
}

int CQueryDataCmdThread::start()
{
	m_bStop = false;
	m_status = false;
	IThread::start();
	return 1;
}
int CTTNTQueryDataCmdThread::stop()
{
	if (m_status) {
		return 1;
	}

	m_bStop = true;
	IThread::join();
	return 1;
}

void CQueryDataCmdThread::run()
{
	while (true) {
		//[1]
		if (m_bStop) {
			m_status = true;
			break;
		}

		//[2]
		sendQueryCmd();

		//[3]
		std::this_thread::sleep_for(std::chrono::milliseconds(200));
	}
}

void  CQueryDataCmdThread::encodeData()
{
	//...
}

void  CTTNTQueryDataCmdThread::sendQueryCmd()
{
	//...
}

2.使用std::jthread创建线程

        std::jthread 是 C++20 标准库中引入的一个新特性,它代表了一个可加入的线程(joinable thread),它确保了线程在其生命周期内始终运行一个特定的任务(即一个函数对象、lambda 表达式或者可调用对象)。std::jthread 的设计旨在简化多线程编程中的一些常见模式,特别是那些需要确保线程在其生命周期内始终运行的任务。

  std::jthread std::thread 基础上,增加了能够主动取消或停止线程执行的新特性。与 std::thread 相比,std::jthread 具有异常安全的线程终止流程,并且在大多数情况下可以替换它,只需很少或无需更改代码。

        示例代码如下:

#include <iostream>
#include <chrono>
#include <thread>

// 使用 std::jthread 运行的函数
void task(std::stop_token stoken) {
    while (!stoken.stop_requested()) {
        std::cout << "任务正在运行..." << std::endl;
        // 模拟一些工作
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    std::cout << "任务已收到停止请求,现在停止运行。" << std::endl;
}

int main() {
    // 创建 std::jthread,自动处理停止令牌
    std::jthread worker(task);

    // 模拟主线程运行一段时间后需要停止子线程
    std::this_thread::sleep_for(std::chrono::seconds(5));
    std::cout << "主线程请求停止子线程..." << std::endl;
    
    // 触发停止请求
    worker.request_stop();

    // std::jthread 在析构时自动加入
    return 0;
}

有了std::thread,为什么还需要引入std::jthread?-CSDN博客

3.定义消息类型的方式

spdlog一个非常好用的C++日志库(五): 源码分析之线程池thread_pool_spdlog源码分析-CSDN博客

        在spdlog的线程池thread_pool源码分析一文中,首先定义了消息类型:

enum class async_msg_type
{
    log,           //普通日志消息
    flush,         //冲刷日志消息到目标(sink)
    terminate      //终止线程池子线程(工作线程)
};

接下来就是在thread_pool的析构函数出提交一条async_msg_type::terminate消息,如下面代码:

SPDLOG_INLINE thread_pool::~thread_pool()
{
    // 析构函数不要抛出异常, 但释放线程池资源资源可能发生异常, 因此内部捕获并处理
    SPDLOG_TRY
    {
        for (size_t i = 0; i < threads_.size(); i++) {
            // terminate thread loop
            post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
        }
 
        for (auto & t : threads_) {
            t.join();
        }
    }
    SPDLOG_CATCH_STD
}

最后在单个线程循环中,检测到async_msg_type::terminate消息,就退出线程,代码如下:

// 子线程循环
void SPDLOG_INLINE thread_pool::worker_loop_() 
{
    while (process_next_msg_()) {}
}
 
// process next message in the queue
// return true if this thread should still be active (while no terminate msg
// was received)
bool SPDLOG_INLINE thread_pool::process_next_msg_()
{
    async_msg incoming_async_msg;
    bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10)); // 从环形缓冲区取出数据
    if (!dequeued)
    {
        return true;
    }
 
    // 成功取出一条数据存作为异步消息, 根据消息类型分类处理
    switch (incoming_async_msg.msg_type)
    {
    case async_msg_type::log: {       // 处理类别为log的异步消息
        incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
        return true;
    }
    case async_msg_type::flush: {     // 处理类别为flush的异步消息
        incoming_async_msg.worker_ptr->backend_flush_();
        return true;
    }
    case async_msg_type::terminate: { // 处理类别为terminate的异步消息
        return false;
    }
    default: {
        assert(false); // impossible except exception
    }
    }
    return true;
}

4.注意事项

  • 确保在发送停止信号后,主线程等待工作线程实际退出(使用joindetach,但通常使用join以确保资源被正确释放)。
  • 在线程函数内部,确保在退出前释放所有分配的资源,包括动态内存、文件句柄、网络连接等。
  • 避免在多个线程之间共享可变数据,除非使用了适当的同步机制(如互斥锁、读写锁等)。

通过上述方法,你可以实现C++标准线程库中的线程优雅退出。


http://www.kler.cn/a/521856.html

相关文章:

  • Vue 3 + TypeScript 实现父子组件协同工作案例解析
  • redis缓存和springboot缓存包冲突怎么办
  • 【数据结构】动态内存管理函数
  • 【深度之眼cs231n第七期】笔记(三十一)
  • 目标跟踪之sort算法(3)
  • 【Python】第五弹---深入理解函数:从基础到进阶的全面解析
  • three.js+WebGL踩坑经验合集(5.2):THREE.Mesh和THREE.Line2在镜像处理上的区别
  • AndroidCompose Navigation导航精通2-过渡动画与路由切换
  • Python GUI 开发 | PySide6 辅助工具简介
  • 恒源云云GPU服务器训练模型指南
  • 二分算法 (二)
  • Springboot使用复盘
  • 计算机视觉算法实战——车辆速度检测
  • Linux常见问题解决方法--1
  • 度小满Java开发面试题及参考答案 (上)
  • 62.异步编程+Prism
  • 数据结构实战之线性表(一)
  • 【算法】多源 BFS
  • YOLOv8:目标检测与实时应用的前沿探索
  • HTML5使用favicon.ico图标
  • android 的aab包
  • 2015年蓝桥杯第六届CC++大学B组真题及代码
  • 利用Python中Scapy库分析网络性能
  • 1月27(信息差)
  • 当高兴、尊重和优雅三位一体是什么情况吗?
  • ShenNiusModularity项目源码学习(7:数据库结构)