C++多线程管理:线程封装与守护监控线程实现

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_stateget_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