ConsumerProducer库:高效处理任务队列,提升系统多线程调度性能 (符合Misra C++)

0. ConsumerProducer库概述

ConsumerProducer库是一个用于多线程任务处理的C++库。它提供了一种机制,允许用户定义任务的优先级和处理方式,并通过多线程方式高效地处理任务队列中的任务。

  • 代码符合Misra C++标准;
  • 模块提供设置线程优先级、处理线程个数及任务队列个数的功能;
  • 模块提供低优先级队列和高优先级队列管理功能。
  • 生产者添加任务的时候会根据优先级添加到低优先级队列还是高优先级队列,消费者获取任务的时候优先获取高优先级队列中的任务进行处理。
  • 模块具有统计任务总的等待时间消耗,处理时间消耗、丢弃时间消耗信息的功能。

完整代码路径:https://gitee.com/liudegui/consumer-producer

1. 使用示例代码如下

#include "consumer_producer.hpp"

#include <chrono>
#include <iostream>
#include <string>
#include <utility>
// Define your worker class if it's not provided
struct YourWorker {
  int id;
  std::string data;
  // Add necessary members and methods
  void process() {
    // Implement processing logic
    std::cout << "Processing data: " << data << std::endl;
  }
};

int32_t consume_func(std::shared_ptr<YourWorker> in_job, bool prefer) {
  // Implement your consume logic here
  // This function will be called by the ConsumerProducer
  if (in_job != nullptr) {
    in_job->process();  // Process the job
    return 0;           // Return appropriate value based on your logic
  } else {
    return -1;  // Indicate failure if job is nullptr
  }
}

int main() {
  // Define your configuration
  MyTest::ConsumerProducerConfig config;
  config.in_name = "YourConsumerProducer";  // Name of the ConsumerProducer
  config.in_priority = 0;                   // Priority of the threads
  config.in_worker_num = 4;                 // Number of threads to create
  config.in_queue_size = 100;               // Size of the queue for jobs
  config.in_prefer_queue_size = 10;         // Size of the preferred jobs in the low priority queue
  config.in_hi_cp_queue_size = 20;          // Size of the queue for high priority jobs
  config.in_cp_cpu_set_size = 0;            // Size of the CPU set (0 for default)
  config.in_cp_cpuset = nullptr;            // CPU set to bind the threads (nullptr for default)
  config.in_allow_log = true;               // Allow logging

  // Create an instance of ConsumerProducer
  MyTest::ConsumerProducer<YourWorker> consumerProducer(config, consume_func);

  // Start processing jobs
  consumerProducer.start_process();

  // Add some jobs to the queue
  for (int i = 0; i < 50; ++i) {
    std::shared_ptr<YourWorker> job_ptr = std::make_shared<YourWorker>();
    job_ptr->id = i;
    job_ptr->data = "Data " + std::to_string(i);
    consumerProducer.add_job(std::move(job_ptr));  // Transfer ownership
  }

  // Wait for some time or do other operations

  // Shutdown the threads
  consumerProducer.shutdown_threads();

  return 0;
}

2. ConsumerProducerConfig结构体

首先,让我们来看一下ConsumerProducerConfig结构体。这个结构体定义了任务处理的配置参数,如输入队列的名称、优先级、工作线程数量等。通过配置这些参数,用户可以根据实际需求灵活地调整任务处理的行为。

3. ConsumerProducer类

ConsumerProducer类是ConsumerProducer库的核心组件,它提供了任务的添加、处理和管理功能。该类包含了任务队列、线程管理等重要功能,下面我们将详细介绍其主要成员和功能。

4. MyJob结构体

MyJob结构体表示一个任务,包含了任务的相关信息,如任务指针、时间戳、任务ID等。通过MyJob结构体,用户可以轻松地管理和操作任务。

5. MyCpQueue类

MyCpQueue类实现了一个任务队列,用于存储任务并支持任务的添加和弹出操作。该类采用循环队列的方式实现,保证了任务的高效处理。

6. ConsumerProducer类成员函数

ConsumerProducer类提供了一系列成员函数,用于任务的添加、处理和管理。这些函数包括添加任务、启动处理线程、暂停任务处理等,为用户提供了丰富的操作接口。

函数名称功能
ConsumerProducer:: ConsumerProducer构造函数
ConsumerProducer::add_job添加job到队列
ConsumerProducer:: add_job_wait_done添加job并等待任务完成
ConsumerProducer:: shutdown关闭线程
ConsumerProducer:: queue_length获取队列中存在的job数量
ConsumerProducer:: max_queue_length获取队列可以存放的最大job数量
ConsumerProducer:: get_threads获取线程池中存放线程容器的首地址
ConsumerProducer:: dropped_job_count获取丢弃的job数量
ConsumerProducer:: blocked_job_count获取被阻塞的job数量
ConsumerProducer:: print_stats打印线程池状态信息

7. 核心代码


struct ConsumerProducerConfig {
  std::string in_name;
  int32_t in_priority;
  uint32_t in_worker_num;
  uint32_t in_queue_size;
  uint32_t in_prefer_queue_size;
  uint32_t in_hi_cp_queue_size;
  uint32_t in_cp_cpu_set_size;
  const cpu_set_t *in_cp_cpuset;
  bool in_allow_log;
};
template <typename Worker>
class ConsumerProducer {
 public:
  /**
   * @brief ConsumerProducer process callback function
   * @param job       MyJob to process
   * @param prefer    True if the job is preferred, otherwise false
   */
  using ConsumeFunc = std::function<int32_t(std::shared_ptr<Worker> job, bool prefer)>;

 private:
  enum MyJobPriority : int32_t {
    PRIORITY_LOW = 0,
    PRIORITY_HIGH = 1,
    PRIORITY_MAX = 2,
  };

  class MyJob {
   private:
    std::shared_ptr<Worker> my_job_ptr_;
    int64_t job_timestamp_;
    uint64_t job_job_id_;
    bool job_not_discardable_;

   public:
    MyJob() : my_job_ptr_(nullptr), job_timestamp_(0), job_job_id_(0), job_not_discardable_(false) {
    }
    MyJob(std::shared_ptr<Worker> job_task, int64_t in_timestamp, uint64_t job_id, bool not_discardable)
        : my_job_ptr_(job_task),
          job_timestamp_(in_timestamp),
          job_job_id_(job_id),
          job_not_discardable_(not_discardable) {
    }

   public:
    inline bool is_not_discardable() const {
      return job_not_discardable_;
    }
    inline uint64_t get_job_id() const {
      return job_job_id_;
    }
    inline int64_t get_timestamp() const {
      return job_timestamp_;
    }
    std::shared_ptr<Worker> get_my_job() const {
      return my_job_ptr_;
    }
  };

  class MyCpQueue {
   private:
    std::string cpqueue_name_;
    uint32_t cpqueue_head_{0};
    uint32_t cpqueue_tail_{0};
    bool cpqueue_full_{false};
    bool cpqueue_empty_{true};
    uint32_t cpqueue_queue_size_{0};
    uint32_t cpqueue_count_{0};
    std::vector<MyJob> jobs_;
    bool cp_queue_allow_log_{false};

   public:
    MyCpQueue(const std::string &in_name, uint32_t in_queue_size, bool in_allow_log) {
      cpqueue_name_ = in_name;
      cpqueue_queue_size_ = in_queue_size;
      jobs_.resize(in_queue_size);

      cp_queue_allow_log_ = in_allow_log;
    }

    ~MyCpQueue() {
      if (cp_queue_allow_log_) {
        if (cpqueue_count_ > 0) {
          MY_LOG_ERROR("%s cpqueue_count_ = %d", cpqueue_name_.c_str(), cpqueue_count_);
        }
      }
    }

    bool is_full() const {
      return cpqueue_full_;
    }
    bool cpq_is_empty() const {
      return cpqueue_empty_;
    }
    uint32_t cp_queue_queue_length() const {
      return cpqueue_count_;
    }
    void cpq_add_job(const MyJob &in_job) {
      jobs_[cpqueue_tail_] = in_job;

      cpqueue_tail_++;
      if (cpqueue_tail_ == cpqueue_queue_size_) {
        cpqueue_tail_ = 0;
      }
      if (cpqueue_tail_ == cpqueue_head_) {
        cpqueue_full_ = true;
      }
      cpqueue_empty_ = false;
      cpqueue_count_++;
      if (cp_queue_allow_log_) {
        if (cpqueue_count_ > cpqueue_queue_size_) {
          MY_LOG_PANIC("%s cpqueue_count_ = %u cpqueue_queue_size_ = %u", cpqueue_name_.c_str(), cpqueue_count_,
                       cpqueue_queue_size_);
        }
      }
      return;
    }

    uint32_t pop(MyJob &out_job, bool peek_only = false) {
      uint32_t ret_value = cpqueue_count_;

      if (cpqueue_empty_) {
        if (cp_queue_allow_log_) {
          if (cpqueue_count_ > 0) {
            MY_LOG_PANIC("%s cpqueue_count_ = %u", cpqueue_name_.c_str(), cpqueue_count_);
          }
        }
        ret_value = 0;
      } else {
        if (cp_queue_allow_log_) {
          if (cpqueue_count_ == 0) {
            MY_LOG_PANIC("%s cpqueue_count_ = %u", cpqueue_name_.c_str(), cpqueue_count_);
          }
        } else {
        }
        out_job = jobs_[cpqueue_head_];
        if (peek_only == false) {
          cpqueue_head_++;
          if (cpqueue_head_ == cpqueue_queue_size_) {
            cpqueue_head_ = 0;
          }
          cpqueue_count_--;
          if (cpqueue_head_ == cpqueue_tail_) {
            cpqueue_empty_ = true;
            if (cp_queue_allow_log_) {
              if (cpqueue_count_ != 0) {
                MY_LOG_PANIC("%s cpqueue_count_ = %u", cpqueue_name_.c_str(), cpqueue_count_);
              }
            }
          }
        }
      }
      cpqueue_full_ = false;

      return ret_value;
    }

    inline uint32_t peek(MyJob &out_job) {
      return pop(out_job, true);
    }
  };

 private:
  std::string cp_name_;
  int32_t cp_priority_;
  MyCpQueue cp_queue_;
  std::atomic_bool blocking_;
  std::atomic_bool paused_;
  uint32_t cp_prefer_queue_size_;
  MyCpQueue cp_hi_queue_;
  uint32_t cp_worker_num_;
  std::vector<std::thread> threads_;
  ConsumeFunc cp_consume_func_;
  std::mutex cp_mutex_;
  std::condition_variable cp_not_full_;
  std::condition_variable cp_not_empty_;
  std::condition_variable cp_job_done_cond_;
  std::atomic_int cp_started_;
  std::atomic_bool cp_shutdown_;
  uint32_t cp_cpusetsize_;
  const cpu_set_t *cp_cpuset_ = nullptr;
  bool cp_allow_log_;
  int64_t start_time_;
  int64_t total_wait_time_[PRIORITY_MAX];
  int64_t total_process_time_[PRIORITY_MAX];
  int64_t total_drop_time_[PRIORITY_MAX];
  uint64_t added_job_[PRIORITY_MAX];
  uint64_t finished_job_[PRIORITY_MAX];
  uint64_t blocked_job_[PRIORITY_MAX];
  uint64_t dropped_job_[PRIORITY_MAX];
  uint32_t cp_job_id_[PRIORITY_MAX];
  uint64_t finished_job_id_[PRIORITY_MAX];
  uint32_t max_queue_length_;
  int64_t last_active_time_;
  int64_t last_elapse_time_;
  int32_t cp_pid_{0};

 private:
  /**
   * @brief Get the job id of corresponding priority
   * @param arry_idx Index of the priority
   * @return Return the job id of corresponding priority
   */
  uint32_t assign_job_id_(uint64_t arry_idx) {
    return ++cp_job_id_[arry_idx];
  }
  /**
   * @brief Mark the last finished job id of corresponding priority
   * @param arry_idx Index of the priority
   * @param id  MyJob id to mark
   */
  void update_done_job_id_(uint64_t arry_idx, uint64_t id_in) {
    finished_job_id_[arry_idx] = id_in;
  }
  /**
   * @brief Get a job from high priority queue or low priority queue
   * @param job      Buffer to store the job
   * @param priorty  Buffer to store the priority of the job
   * @return Return the queue size where the job is from
   */
  uint32_t get_job_(MyJob &out_job, MyJobPriority &priority_out) {
    bool exit_flag = false;
    uint32_t ret_value = 0;
    while (exit_flag == false) {
      std::unique_lock<std::mutex> lck(cp_mutex_);
      if (cp_queue_.cpq_is_empty() && cp_hi_queue_.cpq_is_empty() && (cp_shutdown_.load() == false)) {
        cp_not_empty_.wait(lck);
      }
      ret_value = cp_hi_queue_.pop(out_job);
      if (ret_value == 0) {
        priority_out = PRIORITY_LOW;
        ret_value = cp_queue_.pop(out_job);
      } else {
        priority_out = PRIORITY_HIGH;
      }
      lck.unlock();
      if (ret_value != 0) {
        cp_not_full_.notify_all();
        exit_flag = true;
      } else {
        if (cp_shutdown_) {
          cp_not_empty_.notify_all();
          exit_flag = true;
          ret_value = 0;
        } else {
          // do nothing
        }
      }
    }
    return ret_value;
  }
  /**
   * @brief Get the total number of added jobs
   * @return Return the total number of added jobs
   */
  uint64_t added_job_total_() const {
    uint64_t ret_value = 0;
    for (int32_t arry_index = 0; arry_index < PRIORITY_MAX; arry_index++) {
      ret_value += added_job_[arry_index];
    }
    return ret_value;
  }

  /**
   * @brief Get the total number of finished jobs
   * @return Return the total number of finished jobs
   */
  uint64_t finished_job_total_() const {
    uint64_t ret_value = 0;
    for (int32_t arry_index = 0; arry_index < PRIORITY_MAX; arry_index++) {
      ret_value += finished_job_[arry_index];
    }
    return ret_value;
  }
  /**
   * @brief Get the total number of dropped jobs
   * @return Return the total number of dropped jobs
   */
  uint64_t dropped_job_total_() const {
    uint64_t ret_value = 0;
    for (uint32_t arry_index = 0; arry_index < PRIORITY_MAX; arry_index++) {
      ret_value += dropped_job_[arry_index];
    }
    return ret_value;
  }

 public:
  static void *consumer_thread_func_(ConsumerProducer *in_context);

 public:
  /**
   * @brief ConsumerProducer  ructor.
   *        MyJob in high priority queue will be processed first, and will be marked as preferred.
   *        MyJob in low priority queue will be marked as preferred
   *        if the size of low priority queue is less or equal than prefer_queue_size,
   *        others will be marked as not preferred.
   * @param name              Name of the ConsumerProducer
   * @param priority          MyJobPriority of the threads
   * @param worker_num        Number of threads to create
   * @param consume_func      Process function of the threads
   * @param consume_context   Context of the process function
   * @param queue_size        Size of the queue for jobs
   * @param prefer_queue_size Size of the prefered jobs in the low priority queue
   * @param hi_cp_queue_size  Size of the queue for high priority jobs
   * @param cpusetsize        Size of the CPU set
   * @param cpuset            CPU set to bind the threads
   * @param cp_allow_log_     Allow logging
   */
  explicit ConsumerProducer(const ConsumerProducerConfig &in_config, const ConsumeFunc &in_consume_func);

  ~ConsumerProducer() {
    if (cp_allow_log_) {
      if ((!cp_shutdown_.load()) || paused_.load()) {
        MY_LOG_ERROR("%s shutdown=%d paused=%d started=%d", cp_name_.c_str(), cp_shutdown_.load(), paused_.load(),
                     cp_started_.load());
      }
    }
  }

  /**
   * @brief Add job to the queue
   * @param in                MyJob to add_job
   * @param high_priority     True if the job is high priority, otherwise false.
   * @param job_id_out        Buffer to store job id of the added job
   * @param not_discardable   True if the job is not discardable, otherwise false.
   * @return  Return 0: normal enqueue
   *                 1: block enqueue
   *                 2: give up enqueue
   *                 3: discard head and enqueue
   */
  int32_t add_job_do_(std::shared_ptr<Worker> job, bool high_priority, uint64_t *const job_id_out,
                      bool not_discardable);

  /**
   * @brief Create threads and start processing jobs
   */
  void start_process() {
    cp_started_++;
    cp_shutdown_ = false;
    // creates threads
    threads_.reserve(cp_worker_num_);
    for (uint64_t arry_index = 0; arry_index < cp_worker_num_; arry_index++) {
      threads_.emplace_back(consumer_thread_func_, this);
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
  }

  /**
   * @brief Add job to the queue
   * @param in_job          MyJob to add_job
   * @param high_priority   True if the job is high priority, otherwise false.
   *                        High priority job will be added to high priority queue,
   *                        otherwise low priority queue.
   *                        If the high priority queue is full, the high priority job will wait until
   *                        the queue is not full.
   * @param not_discardable True if the job is not discardable, otherwise false.
   *                        If the low priority queue is full, and not_discardable is true,
   *                        the low priority job will wait until the queue is not full.
   *                        If the low priority queue is full, and not_discardable is false,
   *                        then will comsume the oldest job if possible, otherwise will cosume the
   *                        current job immediately.
   * @return Return 0: normal enqueue
   *                1: block enqueue
   *                2: give up enqueue
   *                3: discard head and enqueue
   */
  inline void add_job(std::shared_ptr<Worker> in_job, bool high_priority = false, bool not_discardable = false);
  /**
   * @brief Add job to the quque and wait until the job is processed
   * @param in_job        MyJob to add_job
   * @param high_priority True if the job is high priority, otherwise false.
   * @return Return 0: normal enqueue
   *                1: block enqueue
   *                2: give up enqueue
   *                3: discard head and enqueue
   */
  int32_t add_job_wait_done(std::shared_ptr<Worker> in_job, bool high_priority = false);
  /**
   * @brief Shutdown the threads and wait until all jobs are stopped
   */
  void shutdown_threads();
  /**
   * @brief Pause adding jobs to the queue, and wait until all jobs are processed or dropped
   */
  void flush_and_pause();
  /**
   * @brief Resume adding jobs to the queue
   */
  void resume();
  /**
   * @brief Get the normal queue length
   * @return Return the normal queue length
   */
  inline uint64_t queue_length() {
    return cp_queue_.cp_queue_queue_length();
  }
  /**
   * @brief Get the normal queue max length
   * @return Return the normal queue max length
   */
  inline uint64_t max_queue_length() {
    return max_queue_length_;
  }
  /**
   * @brief  Get the total number of dropped jobs
   * @return Return the total number of dropped jobs
   */
  inline uint64_t dropped_job_count();
  /**
   * @brief  Get the total number of blocked jobs
   * @return Return the total number of blocked jobs
   */
  inline uint64_t blocked_job_count();
  /**
   * @brief Print current status
   */
  void print_stats(void);
  /**
   * @brief Get current status string
   * @param str_buf       Buffer to store the status string
   */
  void get_stats_string(std::string &output_buffer);
};
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

橘色的喵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值