c++多线程下的简单任务队列

 1、基于 C++11 多线程的单例任务队列实现

在这篇文章中,我们将使用 C++11 标准库中的多线程特性实现一个简单的任务队列。任务队列采用**懒汉单例模式**实现,以确保只有一个实例在程序中被创建。我们会使用 `mutex` 和 `lock_guard` 实现线程安全的任务存取操作,并探讨为什么要对共享资源进行加锁。

1. 项目需求

我们将实现一个多线程任务队列,该任务队列包含生产者和消费者线程。生产者线程负责将任务添加到队列中,而消费者线程从队列中取出任务并处理。使用单例模式是为了确保任务队列在整个程序中是唯一的对象,从而便于管理。

 2. 代码实现
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
using namespace std;

class TaskQueue
{
public:
    TaskQueue(const TaskQueue&) = delete;
    TaskQueue& operator=(const TaskQueue&) = delete;

	static TaskQueue* getInstance()//单列: 懒汉模式
    {
        static TaskQueue tq;
        return &tq;
    }

	bool isEmpty() // 判断队列是否为空
    {
		lock_guard<mutex> lock(m_mutex);//自动加锁解锁
        return m_queue.empty();
    }

	void pushTask(int val)//添加任务
    {
        lock_guard<mutex> lock(m_mutex);
        m_queue.push(val);
    }

    bool popTask()//取出任务
    {
        lock_guard<mutex> lock(m_mutex);
        if (m_queue.empty())
        {
            return false;
        }
        m_queue.pop();
        return true;
    }

	int getTask()//获取任务
    {
        lock_guard<mutex> locker(m_mutex);
        return m_queue.front();
    }

private:
    TaskQueue() {}  // 将构造函数设为私有
	queue<int> m_queue;//任务队列
	mutex m_mutex;//互斥锁
};

int main()
{
	TaskQueue* tq = TaskQueue::getInstance();//获取单列对象

	// 生产者线程
	thread t1([=]() {
        for (int i = 0; i < 10; i++)
        {
            tq->pushTask(i);
            cout << "push task: " << i << "  "
                << this_thread::get_id() << endl;
            this_thread::sleep_for(chrono::milliseconds(500));//延时500ms
        }
        });
	// 消费者线程
    thread t2([=]() {
        this_thread::sleep_for(chrono::milliseconds(100));//延时100ms
        while (!tq->isEmpty())
        {
            int num = tq->getTask();
            cout << "pop task: " << num << "  "
                << this_thread::get_id() << endl;
            tq->popTask();
            this_thread::sleep_for(chrono::milliseconds(1000));
        }
        });

	t1.join();//等待线程结束
	t2.join();//等待线程结束

    return 0;
}
3. 代码解析
 3.1 单例模式

`TaskQueue` 类采用**懒汉单例模式**,即只有在首次调用 `getInstance()` 时才会创建对象。通过将构造函数设为私有,并且禁止拷贝构造和赋值操作,我们确保了 `TaskQueue` 对象的唯一性。

 3.2 互斥锁的使用

我们使用 `mutex` 和 `lock_guard` 来保证线程安全。具体来说:

- **`mutex`**:互斥锁,用于确保每次只有一个线程能够访问 `m_queue`。
- **`lock_guard`**:RAII风格的锁管理类,在对象构造时加锁,在析构时自动解锁,避免手动解锁时可能出现的错误。

 3.3 加锁的目的

在多线程中,如果没有对共享资源加锁,会导致多个线程同时访问 `m_queue`。可能出现的并发问题包括:
- **数据竞争**:两个线程可能同时尝试修改任务队列,导致数据不一致。
- **资源争用**:没有加锁时,队列可能被多个线程同时访问,导致意想不到的行为。

 4. 运行效果

在运行此代码后,生产者线程每隔 500 毫秒向任务队列中添加一个任务,消费者线程每隔 1000 毫秒取出一个任务并打印。加锁确保了每次只有一个线程能够访问任务队列,避免了竞争条件。

总结

本示例展示了如何在多线程环境下实现单例任务队列。并通过使用 `mutex` 和 `lock_guard`,我们能够有效地保护共享资源。

感谢b站up: 爱编程的大丙:https://space.bilibili.com/147020887

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值