C++ 简单的任务队列详解
本文介绍了在 C++ 中实现一个简单的任务队列,用来处理异步任务。任务队列常用于多线程编程中,能够提高程序的并发性能。在本文中,我们将详细介绍任务队列的实现思路和步骤。
实现思路
任务队列是一个先进先出(FIFO)的数据结构,通常实现方式是使用队列。任务队列中存储的是待执行的任务。每当一个任务完成后,就从队列中取出下一个任务执行。实现一个简单的任务队列需要以下的步骤:
- 定义任务结构体(Task)
- 定义任务队列结构体(TaskQueue)
- 实现向任务队列中添加任务的函数(AddTask)
- 实现从任务队列中取出任务的函数(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;
}
在该示例中,我们创建了一个任务队列,并向队列中添加两个简单的任务函数 printHello
和 printWorld
。接着我们启动一个线程不断取出队列中的任务并执行。在主线程中,我们从任务队列中取出任务并执行。
示例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;
}
在该示例中,我们创建了一个任务队列,并向队列中添加十个简单的任务函数 printHello
和 printWorld
。接着我们启动一个线程不断取出队列中的任务并执行。在主线程中,我们从任务队列中取出任务并执行,待所有任务执行完毕后程序结束。
总结
任务队列作为多线程编程的重要组成部分,能够提高程序的并发性能,同时降低代码的复杂度。本文以 C++ 语言为例,介绍了一个简单而又完整的任务队列实现。我们相信这篇文章对于那些初学多线程编程的开发者们会有所帮助。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C++ 简单的任务队列详解 - Python技术站