C++多线程:使用std::condition_variable实现多生产者-多消费者模型示例
贺志国
示例代码如下:
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <queue>
#include <sstream>
#include <thread>
#include <vector>
using namespace std::literals::chrono_literals;
struct CustomOut : public std::stringstream {
public:
~CustomOut() {
std::lock_guard<std::mutex> lock(cout_mutex_);
std::cout << rdbuf();
}
private:
static std::mutex cout_mutex_;
};
std::mutex CustomOut::cout_mutex_;
class MultiProducerConsumer {
public:
MultiProducerConsumer() = default;
~MultiProducerConsumer() = default;
public:
void Stop() { production_stopped_.store(true); }
void Produce(size_t id, size_t items, size_t stock) {
for (size_t i = 0; i < items; ++i) {
{
std::unique_lock<std::mutex> lock(queue_mutex_);
go_produce_cond_.wait(
lock, [this, stock] { return product_queue_.size() < stock; });
product_queue_.push(id * 100 + i);
CustomOut() << " Produce " << id << " --> item " << std::setw(3)
<< product_queue_.back() << '\n';
}
go_consume_cond_.notify_all();
std::this_thread::sleep_for(90ms);
}
CustomOut() << "EXIT: Produce " << id << '\n';
}
void Consume(size_t id) {
while (!production_stopped_.load() || !product_queue_.empty()) {
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (go_consume_cond_.wait_for(
lock, 1s, [this] { return !product_queue_.empty(); })) {
CustomOut() << " item " << std::setw(3)
<< product_queue_.front() << " --> Consume " << id
<< '\n';
product_queue_.pop();
lock.unlock();
go_produce_cond_.notify_all();
}
}
std::this_thread::sleep_for(130ms);
}
CustomOut() << "EXIT: Consume " << id << "\n";
}
private:
std::atomic<bool> production_stopped_{false};
std::mutex queue_mutex_;
std::queue<size_t> product_queue_;
std::condition_variable go_produce_cond_;
std::condition_variable go_consume_cond_;
};
int main() {
std::vector<std::thread> producers;
std::vector<std::thread> consumers;
MultiProducerConsumer multi_producer_consumer;
auto producer = std::bind(&MultiProducerConsumer::Produce,
&multi_producer_consumer, std::placeholders::_1,
std::placeholders::_2, std::placeholders::_3);
auto consumer = std::bind(&MultiProducerConsumer::Consume,
&multi_producer_consumer, std::placeholders::_1);
for (size_t i = 0; i < 3; ++i) {
producers.emplace_back(producer, i, 15, 5);
}
for (size_t i = 0; i < 5; ++i) {
consumers.emplace_back(consumer, i);
}
for (auto &t : producers) {
t.join();
}
// `t.join()` in `consumers` blocks the current thread until the thread 't'
// finishes its execution. The stop instruction should be generated before
// `t.join()` in `consumers`
multi_producer_consumer.Stop();
for (auto &t : consumers) {
t.join();
}
return 0;
}
CMake构建文件如下:
cmake_minimum_required(VERSION 3.0.0)
project(multi_producer_consumer VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 14)
add_executable(${PROJECT_NAME} ${PROJECT_NAME}.cpp)
find_package(Threads REQUIRED)
target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
include(CTest)
enable_testing()
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
set(CPACK_PROJECT_VERSION ${PROJECT_VERSION})
include(CPack)
运行结果如下:
./multi_producer_consumer
Produce 2 --> item 200
item 200 --> Consume 2
Produce 1 --> item 100
item 100 --> Consume 0
Produce 0 --> item 0
item 0 --> Consume 4
Produce 2 --> item 201
item 201 --> Consume 3
Produce 1 --> item 101
item 101 --> Consume 1
Produce 0 --> item 1
item 1 --> Consume 2
Produce 2 --> item 202
item 202 --> Consume 0
Produce 1 --> item 102
item 102 --> Consume 4
Produce 0 --> item 2
item 2 --> Consume 3
Produce 2 --> item 203
Produce 1 --> item 103
item 203 --> Consume 1
item 103 --> Consume 2
Produce 0 --> item 3
item 3 --> Consume 4
Produce 2 --> item 204
item 204 --> Consume 3
Produce 1 --> item 104
item 104 --> Consume 0
Produce 0 --> item 4
item 4 --> Consume 1
Produce 2 --> item 205
Produce 1 --> item 105
item 205 --> Consume 2
item 105 --> Consume 4
Produce 0 --> item 5
item 5 --> Consume 3
Produce 2 --> item 206
Produce 1 --> item 106
item 206 --> Consume 0
Produce 0 --> item 6
item 106 --> Consume 1
item 6 --> Consume 4
Produce 2 --> item 207
item 207 --> Consume 3
Produce 1 --> item 107
item 107 --> Consume 2
Produce 0 --> item 7
item 7 --> Consume 0
Produce 2 --> item 208
Produce 1 --> item 108
item 208 --> Consume 4
item 108 --> Consume 1
Produce 0 --> item 8
item 8 --> Consume 3
Produce 2 --> item 209
Produce 1 --> item 109
Produce 0 --> item 9
item 209 --> Consume 0
item 109 --> Consume 2
item 9 --> Consume 4
Produce 2 --> item 210
item 210 --> Consume 3
Produce 1 --> item 110
item 110 --> Consume 1
Produce 0 --> item 10
item 10 --> Consume 0
Produce 2 --> item 211
Produce 1 --> item 111
Produce 0 --> item 11
item 211 --> Consume 2
item 111 --> Consume 4
item 11 --> Consume 3
Produce 2 --> item 212
Produce 1 --> item 112
item 212 --> Consume 1
Produce 0 --> item 12
item 112 --> Consume 0
item 12 --> Consume 2
Produce 2 --> item 213
Produce 1 --> item 113
item 213 --> Consume 4
Produce 0 --> item 13
item 113 --> Consume 3
item 13 --> Consume 1
Produce 2 --> item 214
Produce 1 --> item 114
item 214 --> Consume 2
item 114 --> Consume 0
Produce 0 --> item 14
item 14 --> Consume 4
EXIT: Produce 1
EXIT: Produce 2
EXIT: Produce 0
EXIT: Consume 2
EXIT: Consume 0
EXIT: Consume 4
EXIT: Consume 3
EXIT: Consume 1