HiJobQueue:一个简单的线程安全任务队列

news/2025/1/23 6:59:36/文章来源:https://www.cnblogs.com/Rong-/p/18687042

HiJobQueue:一个简单的线程安全任务队列

概述

HiJobQueue 是一个线程安全的任务队列,用于在多线程环境中管理和执行异步任务。它的设计参考了 Cobalt 项目中的 JobQueue,并做了适当的简化。HiJobQueue 提供了任务推送(push)、任务弹出(pop)、队列退出(quit)等功能,适用于需要异步任务调度的场景。


核心功能

  1. 线程安全

    • 使用 std::mutex 和 std::condition_variable 实现线程安全的任务队列。
  2. 任务调度

    • 支持任务的异步推送和弹出。
  3. 退出机制

    • 提供 quit() 方法,用于安全地停止任务队列。
  4. 跨平台

    • 使用 C++ 标准库实现,不依赖平台特定的 API。

实现代码

以下是 HiJobQueue 的实现代码:

#pragma once#include <mutex>
#include <functional>
#include <queue>
#include <condition_variable>/*** @brief 线程安全的任务队列,用于管理和执行异步任务。*/
class HiJobQueue final {
public:using Job = std::function<void()>; // 任务类型public:HiJobQueue() : is_exit_(false) {}/*** @brief 推送任务到队列。* @param job 要执行的任务。* @return 如果队列已退出,返回 false;否则返回 true。*/bool push(Job job);/*** @brief 从队列中弹出任务。* @param job 用于存储弹出的任务。* @return 如果队列为空且已退出,返回 false;否则返回 true。*/bool pop(Job& job);/*** @brief 获取队列中的任务数量。* @return 队列中的任务数量。*/size_t size();/*** @brief 退出队列,停止任务处理。*/void quit();/*** @brief 检查队列是否已退出。* @return 如果队列已退出,返回 true;否则返回 false。*/bool is_quited();// 禁用拷贝构造函数和赋值运算符HiJobQueue(HiJobQueue&) = delete;HiJobQueue(const HiJobQueue&) = delete;private:bool is_exit_;                 // 队列退出标志std::mutex mutex_;             // 互斥锁,保护队列访问std::condition_variable cond_; // 条件变量,用于任务通知std::queue<Job> queue_;        // 任务队列
};// 实现bool HiJobQueue::push(Job job) {std::lock_guard<std::mutex> locker(mutex_);if (is_exit_) {return false;}queue_.push(std::move(job));cond_.notify_one();return true;
}bool HiJobQueue::pop(Job& job) {std::unique_lock<std::mutex> locker(mutex_);cond_.wait(locker, [this]() { return is_exit_ || !queue_.empty(); });if (is_exit_ && queue_.empty()) {return false;}job = std::move(queue_.front());queue_.pop();return true;
}size_t HiJobQueue::size() {std::lock_guard<std::mutex> locker(mutex_);return queue_.size();
}void HiJobQueue::quit() {std::lock_guard<std::mutex> locker(mutex_);is_exit_ = true;cond_.notify_all();
}bool HiJobQueue::is_quited() {std::lock_guard<std::mutex> locker(mutex_);return is_exit_;
}

测试用例

为了验证 HiJobQueue 的正确性和线程安全性,我们设计了以下测试用例:

测试代码

#include <gtest/gtest.h>
#include <future>
#include <atomic>
#include <thread>
#include <chrono>
#include "hi_job_queue.h"class TestCls {
public:void test(const char* text, int i) {printf("%s-%d\n", text, i);}
};TEST(HiJobQueueTest, ConcurrentPushPop) {HiJobQueue queue;TestCls cls;std::atomic<int> job_count{0}; // 用于统计执行的任务数量// 启动两个线程消费任务auto f1 = std::async(std::launch::async, [&] {HiJobQueue::Job job;while (queue.pop(job)) {job();job_count++;}});auto f2 = std::async(std::launch::async, [&] {HiJobQueue::Job job;while (queue.pop(job)) {job();job_count++;}});// 启动两个线程生产任务auto f3 = std::async(std::launch::async, [&] {for (int i = 0; i < 200; i++) {queue.push(std::bind(&TestCls::test, &cls, "test1", i));std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 跨平台休眠}});auto f4 = std::async(std::launch::async, [&] {for (int i = 0; i < 200; i++) {queue.push(std::bind(&TestCls::test, &cls, "test2", i));std::this_thread::sleep_for(std::chrono::milliseconds(5)); // 跨平台休眠}});// 等待生产任务完成f3.wait();f4.wait();// 退出队列queue.quit();// 等待消费任务完成f1.wait();f2.wait();// 验证所有任务被执行EXPECT_EQ(job_count.load(), 400); // 200 (test1) + 200 (test2)
}TEST(HiJobQueueTest, QuitBehavior) {HiJobQueue queue;// 启动一个线程消费任务auto consumer = std::async(std::launch::async, [&] {HiJobQueue::Job job;while (queue.pop(job)) {job();}});// 推送一些任务for (int i = 0; i < 10; i++) {queue.push([]() {});}// 退出队列queue.quit();// 等待消费线程结束consumer.wait();// 验证队列已退出EXPECT_TRUE(queue.is_quited());// 验证退出后不能再 push 任务EXPECT_FALSE(queue.push([]() {}));
}TEST(HiJobQueueTest, EmptyQueueBehavior) {HiJobQueue queue;// 验证队列为空时的 pop 行为HiJobQueue::Job job;EXPECT_FALSE(queue.pop(job));// 退出队列queue.quit();// 验证退出后 pop 行为EXPECT_FALSE(queue.pop(job));
}

测试用例说明

  1. ConcurrentPushPop

    • 测试多线程环境下 push 和 pop 的并发行为。
    • 验证所有任务是否被正确执行。
  2. QuitBehavior

    • 测试队列退出时的行为。
    • 验证退出后是否不再接受新任务。
  3. EmptyQueueBehavior

    • 测试队列为空时的行为。
    • 验证退出后 pop 的行为。

适用场景

HiJobQueue 适用于以下场景:

  1. 多线程任务调度

    • 在需要将任务分发到多个工作线程执行的场景中,HiJobQueue 可以作为任务调度器使用。
    • 例如:线程池中的任务队列。
  2. 事件驱动架构

    • 在事件驱动的系统中,HiJobQueue 可以用于存储和处理事件。
    • 例如:GUI 应用中的事件队列。
  3. 异步任务处理

    • 在需要异步执行任务的场景中,HiJobQueue 可以用于存储任务并由后台线程处理。
    • 例如:日志系统的异步写入。
  4. 生产者-消费者模型

    • 在生产者-消费者模型中,HiJobQueue 可以作为共享的任务缓冲区。
    • 例如:多线程下载任务的分发。

优缺点分析

优点

  1. 线程安全

    • 使用 std::mutex 和 std::condition_variable 确保多线程环境下的安全性。
  2. 简单易用

    • 提供了简洁的接口(pushpopquit),易于集成到现有项目中。
  3. 跨平台

    • 基于 C++ 标准库实现,不依赖平台特定的 API,具有良好的可移植性。
  4. 退出机制

    • 提供 quit() 方法,可以安全地停止任务队列,避免资源泄漏。
  5. 轻量级

    • 代码简洁,性能开销小,适合对性能要求较高的场景。

缺点

  1. 功能单一

    • 仅支持基本的任务队列功能,不支持优先级调度或任务取消。
  2. 性能瓶颈

    • 在高并发场景下,std::mutex 可能成为性能瓶颈。
    • 如果需要更高的性能,可以考虑无锁队列(如 boost::lockfree::queue)。
  3. 任务类型限制

    • 任务类型为 std::function<void()>,不支持返回值或参数传递。
    • 如果需要更复杂的任务类型,需要自行扩展。
  4. 缺乏任务状态管理

    • 不支持任务的状态管理(如任务完成通知或错误处理)。

总结

HiJobQueue 是一个简单但功能强大的线程安全任务队列,适用于多线程环境中的异步任务调度。通过参考 Cobalt 项目中的 JobQueue,我们实现了一个更轻量级的版本,并通过单元测试验证了其正确性和线程安全性。希望这篇文章能帮助你理解和使用 HiJobQueue

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/873558.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1. 基于单片机从零写bootloader之第1个bootloader(APP无异常向量表)

1. Bootloader烧写在Flash上,APP也在Flash上直接运行,BootLoader直接跳转到APP位置即可。

VScode配置X11转发!让你彻底摆脱显示屏!!!

作者:SkyXZ CSDN:SkyXZ~-CSDN博客 博客园:SkyXZ - 博客园 在我们远程SSH使用开发板或者是服务器开发的时候,你是否总是苦于没有图像显示环境导致OpenCV的cv2.show()操作总是报没有xcb的错?你是否总算是烦恼于在电脑上写完视觉代码想进行验证时还需反复给开发板插…

做题小结-1.23

我今年真的不想打铁了 https://codeforces.com/contest/2049/problem/C这个题 我是优先队列过的 求和次数 最大 实际上 是一个结论题 很重要if ((a + b + c) & 1) {cout << "-1\n";return;}if ((b + a) < c)cout << a + b << nl;elsecout &…

小智带货助手人物模板制作以及抠除视频背景

小智在视频生成过程中,会用到一个必不可少的材料,就是人物模板(可以简单的理解为AI数字人)。人物模板具体要求以及如何制作如下: 1:可以自行用手机录制一段真人出镜的竖版视频,人物上半身出镜即可,建议站立姿势,随意说一段话或者朗读一段文字均可。简单的手势动作以及…

IO复用 select函数

I/O复用使得程序能同时监听多个文件描述符,这对提高程序的性能至关重要。 通常,网络程序在下列情况下需要使用I/O复用技术: 客户端客户端程序要同时处理多个socket。比如非阻塞connect技术。 客户端程序要同时处理用户输入和网络连接。比如聊天室程序。服务器TCP服务器要同时…

25.1.22小记

今天终于涉及到了面向对象中的类与对象的内容,在这里进行简单的记录 封装 : 把数据和对于数据的操作放在一起 对象 : 属性(数据) + 服务(操作) 一般情况,用户只可进行操作,而数据则被保护 自己定义的class可以作为数据类型定义 对象变量是对象的管理者 this : 成员函…

Vue2_引入及基本功能

介绍了 Vue 核心功能,或者说最基本的功能,包括声明式渲染、条件与循环、处理用户输入、组件化应用构建等,声明式渲染包括文本插值和指令两种方法;条件与循环主要是 v-if 和 v-for 这两个指令;处理用户输入涉及 v-on 和 v-model;组件化应用中指明一个组件本质上是一个拥有…

单纯形法原理

单纯形法的原理介绍及python实现代码单纯形法参考连接:单纯形法单纯形法是针对求解线性规划问题的一个算法,这个名称里的 “单纯形” 是代数拓扑里的一个概念,可以简单将“单纯形”理解为一个凸集,标准的线性规划问题(线性规划标准型)可以表示为: \[max\,(or\,min)\quad…

Tomcat 高并发之道原理拆解与性能调优

上帝视角拆解 Tomcat 架构设计,在了解整个组件设计思路之后。我们需要下凡深入了解每个组件的细节实现。从远到近,架构给人以宏观思维,细节展现饱满的美。 上回👉详情点我【Tomcat】Tomcat 架构原理解析到架构设计借鉴 站在上帝视角给大家拆解了 Tomcat 架构设计,分析 To…

Vue3 —— 安装及配置环境

Vue3的安装、配置(✿◕‿◕✿)Vue官网:https://vuejs.org/配置环境终端:Linux和Mac上可以用自带的终端。Windows上推荐用powershell或者cmd。Git Bash有些指令不兼容。安装Node.js:安装地址:https://nodejs.org/en/安装@vue/cli:执行:npm i -g @vue/cli如果执行后面的操作…

二. Redis 超详细的安装教程((七步)一步一步指导,步步附有截屏操作步骤)

二. Redis 超详细的安装教程((七步)一步一步指导,步步附有截屏操作步骤) @目录二. Redis 超详细的安装教程((七步)一步一步指导,步步附有截屏操作步骤)1. Redis 详细安装教程2. Redis 后台基本启动 & 详细的基本使用3. Redis 服务器的关闭和启动的注意事项4. 如何修改 Re…

数据分库分表和迁移方案

在我们业务快速发展的过程中,数据量必然也会迎来突飞猛涨。那么当我们的数据量百倍、千倍、万倍、亿倍增长后,原有的单表性能就不能满足我们日常的查询和写入了,此时数据架构就不得不进行拆分,比如单表拆分成10张表、100张表、单个月分多张表等等。下面我们针对具体案例分析…