C++ 简单的任务队列详解

C++ 简单的任务队列详解

本文介绍了在 C++ 中实现一个简单的任务队列,用来处理异步任务。任务队列常用于多线程编程中,能够提高程序的并发性能。在本文中,我们将详细介绍任务队列的实现思路和步骤。

实现思路

任务队列是一个先进先出(FIFO)的数据结构,通常实现方式是使用队列。任务队列中存储的是待执行的任务。每当一个任务完成后,就从队列中取出下一个任务执行。实现一个简单的任务队列需要以下的步骤:

  1. 定义任务结构体(Task)
  2. 定义任务队列结构体(TaskQueue)
  3. 实现向任务队列中添加任务的函数(AddTask)
  4. 实现从任务队列中取出任务的函数(GetTask)

结构体定义

任务结构体(Task)应该包含以下的信息:

struct Task {
    std::function<void()> func; // 任务执行函数
    bool done; // 任务是否完成
    std::condition_variable cv; // 定义条件变量通知任务是否完成
    std::mutex lock; // 定义互斥锁
};

任务队列结构体(TaskQueue)应该包含以下的信息:

struct TaskQueue {
    std::queue<std::shared_ptr<Task>> tasks; // 存储任务的队列
    std::mutex lock; // 定义互斥锁
    std::condition_variable cv; // 定义条件变量通知有任务可执行
};

添加任务

向任务队列中添加一个任务(AddTask)时,应该将任务封装成一个 std::shared_ptr<Task> 对象,并将该对象加入到任务队列中。

void AddTask(TaskQueue& taskQueue, std::function<void()> taskFunc) {
    std::shared_ptr<Task> task = std::make_shared<Task>();
    task->func = taskFunc;
    task->done = false;
    {
        std::lock_guard<std::mutex> lock(taskQueue.lock);
        taskQueue.tasks.push(task);
    }
    taskQueue.cv.notify_one();
}

在该函数中,先创建一个 std::shared_ptr 对象,然后将任务函数和完成标记赋值给该对象的成员。接着使用锁保护任务队列,将该任务加入到队列中。最后通知所有等待该条件变量的线程有任务可执行。需要注意的是,在添加任务后需要释放锁,否则等待该条件变量的线程将不能获取到锁,无法执行下去。

取出任务

从任务队列中取出一个任务(GetTask)时,应该先检查是否有任务,若有,则返回队头的任务,否则等待条件变量。

std::shared_ptr<Task> GetTask(TaskQueue& taskQueue) {
    std::unique_lock<std::mutex> lock(taskQueue.lock);
    taskQueue.cv.wait(lock, [&]() { return !taskQueue.tasks.empty(); });
    std::shared_ptr<Task> task = taskQueue.tasks.front();
    taskQueue.tasks.pop();
    return task;
}

在该函数中,首先使用 std::unique_lock 对互斥锁进行加锁,并等待条件变量。等待条件变量的函数需要传入互斥锁和一个 lambda 表达式,返回类型为 bool。该 lambda 表达式返回 true时,等待将被唤醒。在该 lambda 表达式中,我们检查队列是否为空,若不为空则返回 true。若为空则等待条件变量唤醒。当条件变量被唤醒后,任务队列中必定有任务,从队列中取出队头的任务并返回。

示例说明

以下是两个简单的示例说明,用于说明任务队列的使用方法。

示例1

#include <iostream>
#include <thread>

#include "task_queue.h"

TaskQueue g_taskQueue;
bool g_done;

void taskThread() {
    while (!g_done) {
        std::shared_ptr<Task> currTask = GetTask(g_taskQueue);
        currTask->func();
        currTask->done = true;
        currTask->cv.notify_all();
    }
}

void printHello() {
    std::cout << "Hello" << std::endl;
}

void printWorld() {
    std::cout << "World" << std::endl;
}

int main() {
    g_done = false;
    std::thread t(taskThread);

    AddTask(g_taskQueue, printHello);
    AddTask(g_taskQueue, printWorld);

    while (true) {
        std::shared_ptr<Task> currTask = GetTask(g_taskQueue);
        if (currTask->done) {
            break;
        }
        currTask->func();
        currTask->cv.notify_all();
    }

    g_done = true;
    t.join();
    return 0;
}

在该示例中,我们创建了一个任务队列,并向队列中添加两个简单的任务函数 printHelloprintWorld。接着我们启动一个线程不断取出队列中的任务并执行。在主线程中,我们从任务队列中取出任务并执行。

示例2

#include <iostream>
#include <thread>

#include "task_queue.h"

TaskQueue g_taskQueue;
bool g_done;

void taskThread() {
    while (!g_done) {
        std::shared_ptr<Task> currTask = GetTask(g_taskQueue);
        currTask->func();
        currTask->done = true;
        currTask->cv.notify_all();
    }
}

void printHello() {
    std::cout << "Hello" << std::endl;
}

void printWorld() {
    std::cout << "World" << std::endl;
}

int main() {
    g_done = false;
    std::thread t(taskThread);

    for (int i = 0; i < 10; i++) {
        AddTask(g_taskQueue, printHello);
        AddTask(g_taskQueue, printWorld);
    }

    while (true) {
        std::shared_ptr<Task> currTask = GetTask(g_taskQueue);
        if (currTask->done) {
            break;
        }
        currTask->func();
        currTask->cv.notify_all();
    }

    g_done = true;
    t.join();
    return 0;
}

在该示例中,我们创建了一个任务队列,并向队列中添加十个简单的任务函数 printHelloprintWorld。接着我们启动一个线程不断取出队列中的任务并执行。在主线程中,我们从任务队列中取出任务并执行,待所有任务执行完毕后程序结束。

总结

任务队列作为多线程编程的重要组成部分,能够提高程序的并发性能,同时降低代码的复杂度。本文以 C++ 语言为例,介绍了一个简单而又完整的任务队列实现。我们相信这篇文章对于那些初学多线程编程的开发者们会有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++ 简单的任务队列详解 - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • C++ 智能指针深入解析

    C++ 智能指针深入解析 什么是智能指针? 在C++中,一个指针指向某个变量,但是由于指针是”裸”的,需要程序员显式地管理其生命周期。为了解决这个问题,C++11引入了智能指针。智能指针的用法和裸指针类似,但是会自动管理指针所指对象的生命周期。 智能指针的分类 C++中常用的智能指针有三种,它们分别是: unique_ptr:独占所有权的智能指针 share…

    C 2023年5月22日
    00
  • C 表达式中的汇编指令

    C语言表达式中的汇编指令,通常可以通过内嵌汇编或者 inline assembly 的方式实现。所谓内嵌汇编,就是将汇编指令嵌入到C语言程序中,与C语句混在一起。这种方式可以很好的利用汇编指令来进行高级优化并完成一些特殊功能。下面就让我们来分别介绍内嵌汇编与 inline assembly 的实现方式以及示例讲解。 内嵌汇编 内嵌汇编可以分为两种方式,一种是…

    C 2023年5月23日
    00
  • C++ Boost Conversion超详细讲解

    C++ Boost Conversion超详细讲解 什么是Conversion? 在C++编程中,Conversion代表着把一个对象转换成另一种对象的操作。Conversion由C++ Core Language v1.05中的12.3章节定义。例如,如果我们需要把一个整数转换成另一个整数类型或者浮点数类型,那么就需要进行Conversion操作。 Boo…

    C 2023年5月23日
    00
  • C语言实现通讯录系统课程设计

    C语言实现通讯录系统课程设计 本次课程设计旨在通过实现一个简单的通讯录系统,帮助初学者巩固C语言基础知识,并初步了解数据结构相关操作和应用。 基本功能 通讯录系统主要包含以下功能: 添加联系人 删除联系人 修改联系人信息 查找联系人 显示所有联系人信息 清空联系人 设计思路 通讯录系统主要使用链表数据结构作为存储方式,并且通过读写文件将链表数据结构永久保存到…

    C 2023年5月23日
    00
  • C语言实现简单的五子棋游戏

    实现简单的五子棋游戏需要掌握C语言的基础知识,并且需要了解图形界面编程的相关知识,接下来我将为大家介绍C语言实现简单的五子棋游戏的完整攻略。 1. 思路分析 实现五子棋游戏主要需要完成以下几个方面的功能:- 绘制棋盘、棋子- 实现用户和计算机的落子功能- 判断胜利条件- 实现悔棋功能- 中途结束游戏 2. 绘制棋盘和棋子 绘制棋盘和棋子可以利用C语言的图形界…

    C 2023年5月23日
    00
  • python基础教程之popen函数操作其它程序的输入和输出示例

    Python基础教程之popen函数操作其他程序的输入和输出示例 什么是popen函数? popen函数是Python中一个用于打开一个进程作为管道的函数。通过它,你可以利用子进程的标准输入、输出、错误流和父进程之间的通信。popen函数本质上使用子进程来读取、写入或处理数据。 如何使用popen函数? 使用popen函数可以通过以下步骤来完成: 导入sub…

    C 2023年5月22日
    00
  • VS2019开发Linux C++程序的实现步骤

    实现步骤: 安装Visual Studio 2019(注意:需要安装Linux工作负载) 在VS中安装Linux C++开发组件 在VS中创建一个新的Linux C++ 项目(例如console应用程序项目) 配置Linux环境,包括SSH连接、CMake、交叉编译器等。可以参考官方文档和其他教程进行配置。 编写C++代码并进行调试。在VS中按F5可启动调试…

    C 2023年5月23日
    00
  • 基于C语言实现学生成绩管理系统

    基于C语言实现学生成绩管理系统完整攻略 1. 掌握C语言基础 要实现学生成绩管理系统,首先需要掌握C语言的基础知识,包括控制流、函数、数组、结构体、指针等等。 2. 设计数据结构 根据学生成绩管理系统的需求,设计合适的数据结构来存储学生信息和成绩。可以使用结构体来存储学生信息,包括学号、姓名、性别、年龄等等;使用数组来存储学生成绩,每个元素代表一个学生的成绩…

    C 2023年5月23日
    00
合作推广
合作推广
分享本页
返回顶部