深入理解cameron314/concurrentqueue的使用场景与最佳实践

深入理解cameron314/concurrentqueue的使用场景与最佳实践

concurrentqueue A fast multi-producer, multi-consumer lock-free concurrent queue for C++11 concurrentqueue 项目地址: https://gitcode.com/gh_mirrors/co/concurrentqueue

前言

cameron314/concurrentqueue是一个高性能的C++无锁队列实现,特别适合多线程环境下的生产者-消费者场景。本文将深入解析该队列的各种使用模式,帮助开发者掌握其核心用法和最佳实践。

基础使用示例

简单队列操作

让我们从最基本的队列操作开始:

ConcurrentQueue<int> q;

// 入队操作
for (int i = 0; i != 123; ++i)
    q.enqueue(i);

// 出队操作
int item;
for (int i = 0; i != 123; ++i) {
    q.try_dequeue(item);
    assert(item == i);
}

这个例子展示了最基本的入队和出队操作。enqueue方法用于添加元素,try_dequeue尝试取出元素。注意try_dequeue会立即返回,如果队列为空则返回false。

多线程基础

生产者-消费者基础模型

ConcurrentQueue<int> q;
int dequeued[100] = { 0 };
std::thread threads[20];

// 生产者线程
for (int i = 0; i != 10; ++i) {
    threads[i] = std::thread([&](int i) {
        for (int j = 0; j != 10; ++j) {
            q.enqueue(i * 10 + j);
        }
    }, i);
}

// 消费者线程
for (int i = 10; i != 20; ++i) {
    threads[i] = std::thread([&]() {
        int item;
        for (int j = 0; j != 20; ++j) {
            if (q.try_dequeue(item)) {
                ++dequeued[item];
            }
        }
    });
}

// 等待所有线程完成
for (int i = 0; i != 20; ++i) {
    threads[i].join();
}

// 处理剩余元素
int item;
while (q.try_dequeue(item)) {
    ++dequeued[item];
}

// 验证所有元素都被正确处理
for (int i = 0; i != 100; ++i) {
    assert(dequeued[i] == 1);
}

这个例子展示了典型的生产者-消费者模式,10个生产者线程和10个消费者线程并发操作队列。注意最后的清理步骤很重要,确保所有元素都被处理。

批量操作提升性能

批量入队和出队

ConcurrentQueue<int> q;
int dequeued[100] = { 0 };
std::thread threads[20];

// 生产者使用批量入队
for (int i = 0; i != 10; ++i) {
    threads[i] = std::thread([&](int i) {
        int items[10];
        for (int j = 0; j != 10; ++j) {
            items[j] = i * 10 + j;
        }
        q.enqueue_bulk(items, 10);
    }, i);
}

// 消费者使用批量出队
for (int i = 10; i != 20; ++i) {
    threads[i] = std::thread([&]() {
        int items[20];
        for (std::size_t count = q.try_dequeue_bulk(items, 20); count != 0; --count) {
            ++dequeued[items[count - 1]];
        }
    });
}

// 等待所有线程
for (int i = 0; i != 20; ++i) {
    threads[i].join();
}

// 处理剩余元素
int items[10];
std::size_t count;
while ((count = q.try_dequeue_bulk(items, 10)) != 0) {
    for (std::size_t i = 0; i != count; ++i) {
        ++dequeued[items[i]];
    }
}

// 验证所有元素
for (int i = 0; i != 100; ++i) {
    assert(dequeued[i] == 1);
}

批量操作可以显著提高性能,特别是在高并发场景下。enqueue_bulktry_dequeue_bulk方法允许一次处理多个元素,减少了锁竞争和上下文切换的开销。

高级生产者-消费者模型

同时生产消费模型

ConcurrentQueue<Item> q;
const int ProducerCount = 8;
const int ConsumerCount = 8;
std::thread producers[ProducerCount];
std::thread consumers[ConsumerCount];
std::atomic<int> doneProducers(0);
std::atomic<int> doneConsumers(0);

// 生产者线程
for (int i = 0; i != ProducerCount; ++i) {
    producers[i] = std::thread([&]() {
        while (produce) {
            q.enqueue(produceItem());
        }
        doneProducers.fetch_add(1, std::memory_order_release);
    });
}

// 消费者线程
for (int i = 0; i != ConsumerCount; ++i) {
    consumers[i] = std::thread([&]() {
        Item item;
        bool itemsLeft;
        do {
            // 内存屏障确保看到所有生产者的状态
            itemsLeft = doneProducers.load(std::memory_order_acquire) != ProducerCount;
            while (q.try_dequeue(item)) {
                itemsLeft = true;
                consumeItem(item);
            }
        } while (itemsLeft || doneConsumers.fetch_add(1, std::memory_order_acq_rel) + 1 == ConsumerCount);
    });
}

// 等待所有线程
for (int i = 0; i != ProducerCount; ++i) {
    producers[i].join();
}
for (int i = 0; i != ConsumerCount; ++i) {
    consumers[i].join();
}

这个模型展示了生产者和消费者同时运行的情况。注意内存顺序的使用(memory_order_acquirememory_order_release)确保正确的内存可见性。

阻塞队列版本

BlockingConcurrentQueue<Item> q;
const int ProducerCount = 8;
const int ConsumerCount = 8;
std::thread producers[ProducerCount];
std::thread consumers[ConsumerCount];
std::atomic<int> promisedElementsRemaining(ProducerCount * 1000);

// 生产者
for (int i = 0; i != ProducerCount; ++i) {
    producers[i] = std::thread([&]() {
        for (int j = 0; j != 1000; ++j) {
            q.enqueue(produceItem());
        }
    });
}

// 消费者
for (int i = 0; i != ConsumerCount; ++i) {
    consumers[i] = std::thread([&]() {
        Item item;
        while (promisedElementsRemaining.fetch_sub(1, std::memory_order_relaxed) > 0) {
            q.wait_dequeue(item);
            consumeItem(item);
        }
    });
}

// 等待线程
for (int i = 0; i != ProducerCount; ++i) {
    producers[i].join();
}
for (int i = 0; i != ConsumerCount; ++i) {
    consumers[i].join();
}

BlockingConcurrentQueue提供了wait_dequeue方法,当队列为空时会阻塞等待。注意需要预先知道元素数量或使用其他协调机制。

实际应用场景

对象池实现

class SomethingPool {
public:
    Something getSomething() {
        Something obj;
        queue.try_dequeue(obj);
        return obj;
    }

    void recycleSomething(Something&& obj) {
        queue.enqueue(std::move(obj));
    }
private:
    ConcurrentQueue<Something> queue;
};

这个对象池实现展示了如何利用并发队列管理可重用对象。try_dequeue尝试获取已有对象,如果没有则返回默认构造对象。

线程池任务队列

BlockingConcurrentQueue<Task> q;

// 提交任务
q.enqueue(...);

// 工作线程处理任务
Task task;
while (true) {
    q.wait_dequeue(task);
    // 处理任务...
}

这是线程池的典型实现,工作线程使用wait_dequeue等待任务到来。

多线程游戏循环

BlockingConcurrentQueue<Task> q;
std::atomic<int> pendingTasks(0);

// 工作线程
Task task;
while (true) {
    q.wait_dequeue(task);
    // 处理任务...
    pendingTasks.fetch_add(-1, std::memory_order_release);
}

// 提交任务
pendingTasks.fetch_add(1, std::memory_order_release);
q.enqueue(...);

// 等待任务完成
while (pendingTasks.load(std::memory_order_acquire) != 0)
    continue;

// 或者帮助处理任务
while (pendingTasks.load(std::memory_order_acquire) != 0) {
    if (!q.try_dequeue(task)) {
        continue;
    }
    // 处理任务...
    pendingTasks.fetch_add(-1, std::memory_order_release);
}

游戏循环需要高效处理大量任务,这个例子展示了如何结合原子计数器和队列实现高效的任务调度。

注意事项与最佳实践

  1. 内存顺序:多线程环境下正确使用内存屏障(memory_order)至关重要
  2. 队列清空:无法可靠地判断队列是否真正为空,除非所有操作都已完成
  3. 批量操作:在性能关键路径上优先考虑批量操作
  4. 阻塞队列:使用BlockingConcurrentQueue时需要有明确的终止条件
  5. 对象生命周期:确保队列中的对象在出队后仍然有效

通过以上示例和分析,开发者可以更好地理解如何在各种场景下高效使用cameron314/concurrentqueue,构建高性能的并发应用程序。

concurrentqueue A fast multi-producer, multi-consumer lock-free concurrent queue for C++11 concurrentqueue 项目地址: https://gitcode.com/gh_mirrors/co/concurrentqueue

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

柏纲墩Dean

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

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

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

打赏作者

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

抵扣说明:

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

余额充值