C++线程安全队列

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;
}
posted @ 2023-07-20 22:28  AE酱  阅读(118)  评论(0编辑  收藏  举报