目錄
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_BEGINclass 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_BEGINIThread::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 looppost_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.注意事項
- 確保在發送停止信號后,主線程等待工作線程實際退出(使用
join
或detach
,但通常使用join
以確保資源被正確釋放)。 - 在線程函數內部,確保在退出前釋放所有分配的資源,包括動態內存、文件句柄、網絡連接等。
- 避免在多個線程之間共享可變數據,除非使用了適當的同步機制(如互斥鎖、讀寫鎖等)。
通過上述方法,你可以實現C++標準線程庫中的線程優雅退出。