threadsafe_queue.h
#pragma once
#include <memory>
#include <queue>
#include <mutex>
#include <condition_variable>
template<typename T>
class threadsafe_queue {
private:
mutable std::mutex mut;
std::queue<T> data_queue;
std::condition_variable data_cond;
public:
threadsafe_queue() = default;
threadsafe_queue(const threadsafe_queue&);
threadsafe_queue& operator=(const threadsafe_queue&) = delete;
void push(T new_value);
bool try_pop(T& value);
std::shared_ptr<T> try_pop();
void wait_and_pop(T& value);
std::shared_ptr<T> wait_and_pop();
bool empty() const;
};
template<typename T>
threadsafe_queue<T>::threadsafe_queue(const threadsafe_queue& other) {
std::lock_guard<std::mutex> lk(mut);
data_queue = other.data_queue;
}
template<typename T>
void threadsafe_queue<T>::push(T new_value) {
std::lock_guard<std::mutex> lk(mut);
data_queue.push(new_value);
data_cond.notify_one();
}
template<typename T>
void threadsafe_queue<T>::wait_and_pop(T& value) {
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk, [this] {return !data_queue.empty(); });
value = data_queue.front();
data_queue.pop();
}
template<typename T>
std::shared_ptr<T> threadsafe_queue<T>::wait_and_pop() {
std::unique_lock<std::mutex> lk(mut);
data_cond.wait(lk, [this] {return !data_queue.empty(); });
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
data_queue.pop();
return res;
}
template<typename T>
bool threadsafe_queue<T>::try_pop(T& value) {
std::lock_guard<std::mutex> lk(mut);
if (data_queue.empty()) return false;
value = data_queue.front();
data_queue.pop();
return true;
}
template<typename T>
std::shared_ptr<T> threadsafe_queue<T>::try_pop() {
std::lock_guard<std::mutex> lk(mut);
if (data_queue.empty()) return std::shared_ptr<T>();
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
data_queue.pop();
return res;
}
template<typename T>
bool threadsafe_queue<T>::empty() const {
std::lock_guard<std::mutex> lk(mut);
return data_queue.empty();
}
main.cpp
#include <iostream>
#include <thread>
#include "threadsafe_queue.h"
struct data_chunk {
bool isLast;
int id;
std::thread::id thread_id;
int value;
};
threadsafe_queue<data_chunk> data_queue;
class data_prepare_thread {
private:
int data_cnt = 0;
static const int max_data_num = 50;
bool more_data_to_prepare() {
return data_cnt < max_data_num;
}
data_chunk prepare_data() {
++data_cnt;
data_chunk data;
data.id = data_cnt;
data.thread_id = std::this_thread::get_id();
data.isLast = (data_cnt == max_data_num);
data.value = data_cnt;
return data;
}
public:
void data_preparation_thread() {
while (more_data_to_prepare()) {
const data_chunk data = prepare_data();
data_queue.push(data);
}
}
};
std::mutex print_mutex;
void process(const data_chunk& data) {
std::lock_guard<std::mutex> lk(print_mutex);
std::cout << "[thread " << data.thread_id<< "]"
<< "[data_chunk " << data.id << "] value = " << data.value << std::endl;
}
bool is_last_chunk(const data_chunk& data) {
return data.isLast;
}
void data_processing_thread() {
int cnt = 0;
while (true) {
data_chunk data;
data_queue.wait_and_pop(data);
process(data);
if (is_last_chunk(data)) ++cnt;
if (cnt == 2) break; // 两个线程的数据全部准备并处理完
}
}
int main() {
data_prepare_thread prepareA, prepareB;
// 用一个线程处理数据
std::thread t1(data_processing_thread);
// 用两个线程准备数据
std::thread t2(&data_prepare_thread::data_preparation_thread, &prepareA);
std::thread t3(&data_prepare_thread::data_preparation_thread, &prepareB);
t1.join();
t2.join();
t3.join();
return 0;
}