-
本文實現了一個帶鎖同步隊列,可以讓多個線程向隊列裏讀寫數據而不會產生異常,很方便的應用於生產者消費者模式的程序中
-
同步隊列沒什麼難的,無非就是讀寫的時候加鎖,並且寫入的時候通知消費者。稍微複雜的地方在於怎麼樣讓生產者消費者停止運行,或者說,怎麼才能讓同步隊列退出;話不多說,直接上代碼:
#ifndef SYNC_QUEUE_H
#define SYNC_QUEUE_H
/*
* 同步隊列,實現了生產者消費者
*/
#include <deque>
#include <pthread.h>
template<typename T>
class SyncQueue{
public:
SyncQueue(){
running_ = true;
pthread_mutex_init(&mutex_,NULL);
pthread_cond_init(&cond_,NULL);
}
~SyncQueue(){
stop();
pthread_cond_destroy(&cond_);
pthread_mutex_destroy(&mutex_);
}
// 入隊
void push_back(T info){
SafeLock guard(mutex_);
if (!running_) {
return;
}
bool empty = data_list_.empty();
data_list_.push_back(info);
if (empty) {
Signal();
}
}
// 返回第一個元素,不等待
bool head(T& val) {
SafeLock guard(mutex_);
if ( data_list_.empty()) {
return false;
}
val = data_list_[0];
return true;
}
// 返回最後一個元素,不等待
bool tail(T& val) {
SafeLock guard(mutex_);
if (data_list_.empty()) {
return false;
}
val = data_list_[data_list_.size() -1];
return true;
}
// 獲取某個元素,不等待
bool get(size_t index, T& val) {
SafeLock guard(mutex_);
if (data_list_.empty()) {
return false;
}
if (index >= data_list_.size()) {
return false;
}
val = data_list_[index];
return true;
}
// 出隊,等待
bool pop_front(T& val){
SafeLock guard(mutex_);
if (!running_ && data_list_.empty()) {
return false;
}
bool ret = false;
// 已經不運行了
if (!running_) {
if (data_list_.empty() == false) {
val = data_list_[0];
data_list_.pop_front();
ret = true;
}
else {
ret = false;
}
return ret;
}
// 還在運行
Wait();
// 到了這一步將有兩種情況:
// 1、隊列中有數據
// 2、隊列退出!
if (running_) {
if (!data_list_.empty()) {
val = data_list_[0];
data_list_.pop_front();
ret = true;
}
else {
// 一般來說這一步是不存在的
ret = false;
}
}
else {
ret = false;
}
return ret;
}
size_t size(){
SafeLock guard(mutex_);
size_t size = data_list_.size();
return size;
}
bool empty(){
SafeLock guard(mutex_);
bool empty = data_list_.empty();
return empty;
}
bool is_running() {
SafeLock guard(mutex_);
return running_;
}
/*void start() {
SafeLock guard(mutex_);
if(running_){
return;
}
//data_list_.clear();
running_ = true;
}*/
// 由數據發送方調用
void stop() {
SafeLock guard(mutex_);
running_ = false;
Broadcast();
}
private:
void Wait(){
while(data_list_.empty() && running_){
pthread_cond_wait(&cond_,&mutex_);
}
}
void Signal(){
pthread_cond_signal(&cond_);
}
void Broadcast() {
pthread_cond_broadcast(&cond_);
}
class SafeLock {
public:
SafeLock(pthread_mutex_t& l):mutex(l) {
pthread_mutex_lock(&mutex);
}
~SafeLock() {
pthread_mutex_unlock(&mutex);
}
private:
pthread_mutex_t& mutex;
};
private:
SyncQueue & operator=(const SyncQueue&) {}
// 隊列鎖和隊列
pthread_mutex_t mutex_;
pthread_cond_t cond_;
std::deque<T> data_list_;
bool running_;
};
#endif // SYNC_QUEUE_H
- 測試代碼
#include <iostream>
#include <thread>
#include <chrono>
#include "sync_queue.h"
using namespace std;
SyncQueue<int> Q;
// 生產者
void produce(){
for(int i = 0; i < 1024; ++i){
Q.push_back(i);
}
// 通知各個消費者,同步隊列已經停止
Q.stop();
}
// 消費者
void consume(){
int val;
while (Q.pop_front(val)) {
printf("val = %d\n",val);
std::this_thread::sleep_for(chrono::milliseconds(100));
}
}
int main()
{
// 創建生產者者線程
thread pth(produce);
pth.detach();
// 創建消費者線程
thread cth1(consume);
thread cth2(consume);
thread cth3(consume);
thread cth4(consume);
thread cth5(consume);
thread cth6(consume);
thread cth7(consume);
thread cth8(consume);
cth1.join();
cth2.join();
cth3.join();
cth4.join();
cth5.join();
cth6.join();
cth7.join();
cth8.join();
return 0;
}