文章目录
0. 引言
本文将介绍如何通过二次封装来管理线程,以及如何利用守护线程机制监控多个线程的状态,确保程序的稳定性和高可用性。
更多请看:自定义C++线程管理类:解决std::thread()线程创建失败的问题
1. 线程的二次封装
1.1 简化线程使用
直接使用 std::thread
来创建线程虽然简单,但对于复杂应用,往往需要更加灵活的控制,比如设置线程优先级、CPU亲和性、线程状态管理等。因此,我们对 std::thread
进行了二次封装。WorkerThread
类负责线程的创建、启动、优先级设置等任务:
class WorkerThread {
public:
WorkerThread(const std::string& in_name, int32_t in_priority,
const std::function<void(WorkerThread*)> in_func, int32_t in_cpusetsize,
const cpu_set_t* const in_cpuset);
void thread_start();
void thread_shutdown();
void set_state(ThreadState state);
ThreadState get_state() const;
};
WorkerThread
类通过构造函数接收线程的配置参数,启动时设置线程的优先级、CPU亲和性等,并通过 set_state
和 get_state
管理线程的状态。
void WorkerThread::thread_start() {
std::lock_guard<std::mutex> lck(thread_mutex_);
is_shutdown_ = false;
task_thread_ = std::make_shared<std::thread>(worker_thread_func_, this);
}
1.2. 线程优先级与CPU亲和性
pthread_setaffinity_np(tid, static_cast<size_t>(thread_ins->thread_cpusetsize_), thread_ins->thread_cpuset_);
pthread_setname_np(tid, thread_ins->thread_name_.c_str());
- 线程优先级:通过
pthread_setschedparam
设置线程的优先级,使高优先级的线程能够在CPU上获得更多的时间片。 - CPU亲和性:通过
pthread_setaffinity_np
设置线程的 CPU 亲和性,将线程绑定到特定的CPU核心上,避免上下文切换带来的性能损失。
1.3. 异常处理与线程稳定性
在每个线程的执行函数中加入了异常处理代码。即使某个线程发生了异常,程序也能继续运行。
try {
thread_ins->func_main_(thread_ins);
} catch (...) {
LOG_ERROR("Exception occurred in thread %s", thread_ins->thread_name_.c_str());
}
2. 守护线程的机制
2.1 守护线程的作用
守护线程是一种在后台持续运行的线程,主要作用是监控其他线程的健康状态。在多线程程序中,线程可能因为某些原因(如异常、死锁等)退出或进入异常状态,守护线程可以帮助我们及时发现这些问题并采取必要的处理。
在我们的实现中,DaemonThread
类充当守护线程的角色。它的主要职责是定期检查所有 WorkerThread
线程的状态,并在某个线程出现异常时进行处理。
class DaemonThread {
public:
static DaemonThread& get_instance();
void append_thread(std::shared_ptr<WorkerThread> src_thred);
void start_monitor();
void monitor_threads();
};
守护线程通过 monitor_threads
方法定期扫描每个工作线程的状态。当某个工作线程异常退出或失败时,守护线程会执行相应的处理。
2.2 守护线程的工作原理
DaemonThread
通过维护一个线程列表 (worker_threads_
) 来跟踪所有的工作线程。在每次监控周期中,守护线程会检查每个线程的状态:
- 如果线程已经退出或被标记为失败,守护线程会执行处理(例如重新启动线程或记录日志)。
- 如果线程运行正常,守护线程会继续等待并重新检查。
void DaemonThread::monitor_threads() {
while (!shutdown_) {
std::unique_lock<std::mutex> lck(monitor_mutex_);
for (auto it = worker_threads_.begin(); it != worker_threads_.end();) {
auto& worker = *it;
if ((worker.task_thread != nullptr) && worker.task_thread->has_shutdown()) {
handle_failure(worker);
it = worker_threads_.erase(it);
} else {
++it;
}
}
lck.unlock();
std::this_thread::sleep_for(std::chrono::milliseconds(kCheckInterval_));
}
}
3. 完整实现
3.1. util_thread.hpp
/**
*/
#ifndef UTILS_MY_THREAD_H_
#define UTILS_MY_THREAD_H_
#include <sys/syscall.h>
#include <sys/types.h>
#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <ctime>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#ifndef LOG_MACROS_H
#define LOG_MACROS_H
#define LOG_PRINT(level, ...) \
ThreadTest::log_print(ThreadTest::LogLevel::level, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
#define LOG_TRACE(...) LOG_PRINT(LOG_LEVEL_TRACE, __VA_ARGS__)
#define LOG_DEBUG(...) LOG_PRINT(LOG_LEVEL_DEBUG, __VA_ARGS__)
#define LOG_INFO(...) LOG_PRINT(LOG_LEVEL_INFO, __VA_ARGS__)
#define LOG_WARN(...) LOG_PRINT(LOG_LEVEL_WARN, __VA_ARGS__)
#define LOG_ERROR(...) LOG_PRINT(LOG_LEVEL_ERROR, __VA_ARGS__)
#define LOG_CRITICAL(...) LOG_PRINT(LOG_LEVEL_CRITICAL, __VA_ARGS__)
#endif // LOG_MACROS_H
namespace ThreadTest {
enum class LogLevel : std::int32_t