C++多线程:使用std::condition_variable实现生产者-消费者模型示例
贺志国
示例代码如下:
#include <atomic>
#include <condition_variable>
#include <functional>
#include <future>
#include <iostream>
#include <queue>
using namespace std::literals::chrono_literals;
class ProducerConsumer {
public:
ProducerConsumer() = default;
~ProducerConsumer() = default;
public:
void Stop() {
std::cout << "is_stop_.is_lock_free() = " << is_stop_.is_lock_free()
<< '\n';
is_stop_.store(true);
task_cond_.notify_one();
}
void Produce(size_t items) {
for (size_t i = 0; i < items; ++i) {
std::this_thread::sleep_for(100ms);
{
std::lock_guard<std::mutex> lk(task_mutex_);
task_queue_.emplace(i);
}
task_cond_.notify_one();
}
// is_stop_.store(true);
// task_cond_.notify_one();
}
void Consume() {
std::unique_lock<std::mutex> lock(task_mutex_);
while (!is_stop_.load()) {
task_cond_.wait(
lock, [this] { return !task_queue_.empty() || is_stop_.load(); });
while (!task_queue_.empty()) {
std::cout << "Got " << task_queue_.front() << " from queue.\n";
task_queue_.pop();
}
}
}
private:
std::atomic<bool> is_stop_{false};
std::queue<size_t> task_queue_;
std::mutex task_mutex_;
std::condition_variable task_cond_;
};
int main() {
ProducerConsumer producer_consumer;
auto producer = std::async(std::launch::async, &ProducerConsumer::Produce,
&producer_consumer, 10);
auto consumer = std::async(std::launch::async, &ProducerConsumer::Consume,
&producer_consumer);
producer.wait();
// `consumer.wait()` blocks the current thread until the thread `consumer`
// finishes its execution. The stop instruction should be generated before
// `consumer.wait()`
producer_consumer.Stop();
consumer.wait();
std::cout << "Finished!\n";
return 0;
}
另一种实现方式的代码如下,大家可根据需要挑选具体的实现方式:
#include <atomic>
#include <condition_variable>
#include <functional>
#include <iostream>
#include <queue>
#include <thread>
#include <tuple>
using namespace std::literals::chrono_literals;
class ProducerConsumer {
public:
ProducerConsumer() = default;
~ProducerConsumer() = default;
public:
void Stop() {
std::cout << "is_stop_.is_lock_free() = " << is_stop_.is_lock_free()
<< '\n';
is_stop_.store(true);
task_cond_.notify_one();
}
void Produce(size_t items) {
for (size_t i = 0; i < items; ++i) {
std::this_thread::sleep_for(100ms);
{
std::lock_guard<std::mutex> lk(task_mutex_);
task_queue_.emplace(i);
}
task_cond_.notify_one();
}
}
void Consume() {
while (!is_stop_.load()) {
std::unique_lock<std::mutex> lock(task_mutex_);
task_cond_.wait(
lock, [this] { return !task_queue_.empty() || is_stop_.load(); });
while (!task_queue_.empty()) {
std::cout << "Got " << task_queue_.front() << " from queue.\n";
task_queue_.pop();
}
}
}
private:
std::atomic<bool> is_stop_{false};
std::queue<size_t> task_queue_;
std::mutex task_mutex_;
std::condition_variable task_cond_;
};
int main() {
ProducerConsumer producer_consumer;
std::thread t1(&ProducerConsumer::Produce, &producer_consumer, 10);
std::thread t2(&ProducerConsumer::Consume, &producer_consumer);
t1.join();
// `t2.join()` blocks the current thread until the thread `t2` finishes its
// execution. The stop instruction should be generated before `t2.join()`
producer_consumer.Stop();
t2.join();
std::cout << "Finished!\n";
return 0;
}
CMake构建文件如下:
cmake_minimum_required(VERSION 3.0.0)
project(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)
运行结果如下:
./producer_consumer
Got 0 from queue.
Got 1 from queue.
Got 2 from queue.
Got 3 from queue.
Got 4 from queue.
Got 5 from queue.
Got 6 from queue.
Got 7 from queue.
Got 8 from queue.
Got 9 from queue.
is_stop_.is_lock_free() = 1
Finished!