### 循环队列和阻塞队列
#### 循环队列
- **定义**: 一个固定大小的数组,通过两个指针(`front` 和 `back`)管理队列的头部和尾部元素。
- **特点**:
- **循环性**: 当指针到达数组的末尾时,可以回绕到数组的开头,从而利用数组的全部空间。
- **空间效率**: 使用固定大小的数组,避免了动态内存分配和释放带来的开销。
#### 阻塞队列
- **定义**: 一种线程安全的队列,在队列为空时尝试出队的线程会被阻塞,在队列已满时尝试入队的线程也会被阻塞。
- **特点**:
- **线程安全**: 使用互斥锁和条件变量来保证多线程环境下的安全访问。
- **阻塞机制**: 当队列为空或已满时,通过条件变量阻塞相应的线程,直到队列状态发生改变。
### 阻塞队列与生产者-消费者模型
#### 生产者-消费者模型
- **定义**: 一种经典的多线程同步模型,通常涉及两个角色:生产者和消费者。
- **生产者**: 负责生产数据并将其放入队列。
- **消费者**: 负责从队列中取出数据进行处理。
- **特点**:
- **解耦**: 生产者和消费者通过共享队列进行数据交换,彼此之间解耦。
- **同步**: 使用阻塞队列来实现生产者和消费者之间的同步。
### 阻塞队列与异步日志系统
#### 异步日志系统
- **定义**: 一种日志记录系统,其中日志记录操作与业务逻辑操作分离,通过异步机制提高性能。
- **特点**:
- **非阻塞**: 业务逻辑线程将日志消息放入队列后立即返回,而不是等待日志写入完成。
- **后台处理**: 专门的日志线程从队列中取出日志消息并写入日志文件。
#### 阻塞队列在异步日志系统中的应用
- **队列**: 用于存放日志消息。
- **生产者**: 业务逻辑线程,将日志消息放入队列。
- **消费者**: 日志处理线程,从队列中取出日志消息并写入日志文件。
- **同步**: 通过阻塞队列实现生产者和消费者的同步,确保日志消息不会丢失且处理有序。
### 代码分析
- **构造函数**: 初始化队列的最大大小和内部数组。
- **析构函数**: 销毁队列,释放内存。
- **清空队列**: 清空队列内容并重置指针。
- **检查队列状态**: 判断队列是否为空或已满。
- **获取队首和队尾元素**: 安全地获取队列的首尾元素。
- **入队和出队操作**: 提供线程安全的入队和出队操作,使用条件变量实现阻塞机制。
总的来说,这个阻塞队列通过互斥锁和条件变量,实现了多线程环境下的安全访问和同步机制,可以用于生产者-消费者模型中,实现高效的异步日志系统。
#ifndef BLOCK_QUEUE_H
#define BLOCK_QUEUE_H
#include<iostream>
#include<stdio.h>
#include<pthread.h>
#include<sys/time.h>
#include"../lock/locker.h"
using namespace std;
template<class T>
class block_queue{
public:
/*初始化阻塞队列*/
block_queue(int max_size) {
if (max_size <= 0) {
exit(-1);
}
m_max_size = max_size;
T* m_array = new T[max_size];
m_size = 0;
m_front = -1;
m_back = -1;
}
/*删除new出的T数组*/
~block_queue() {
m_mutex.lock();
if (m_array != NULL) {
delete []m_array;
}
m_mutex.unlock();
}
/*清空队列*/
void clear() {
m_mutex.lock();
m_size = 0;
m_front = -1;
m_back = -1;
m_mutex.unlock();
}
/*判断队列是否已满*/
bool full() {
m_mutex.lock();
if (m_size >= m_max_size) {
m_mutex.unlock();
return true;
}
m_mutex.unlock();
return false;
}
/*判断队列是否为空*/
bool empty() {
m_mutex.lock();
if (m_size == 0) {
m_mutex.unlock();
return true;
}
m_mutex.unlock();
return false;
}
/*获得队首元素*/
bool front(T &value) {
m_mutex.lock();
/*注意下面的if判断不能用empty,因为empty函数也有加锁操作,加两次锁会导致死锁*/
if (size == 0) {
m_mutex.unlock();
return false;
}
//TODO:个人感觉这行逻辑出错,后面部分是原代码 value = m_array[m_front];
value = m_array[(m_front + 1) % m_max_size];
m_mutex.unlock();
return true;
}
/*获得队尾元素*/
bool back(T& value) {
m_mutex.lock();
if (size == 0) {
m_mutex.unlock();
return false;
}
value = m_array[m_back];
m_mutex.unlock();
return true;
}
int size() {
int tmp = 0;
m_mutex.lock();
tmp = m_size;
m_mutex.unlock();
return tmp;
}
int max_size() {
int tmp = 0;
m_mutex.lock();
tmp = m_max_size;
m_mutex.unlock();
return tmp;
}
/*往队列中添加元素前需要先将所有使用队列的线程先唤醒*/
/*阻塞队列封装了生产者消费者模型,调用push的是生产者,也就是工作线程*/
bool push(T& item) {
m_mutex.lock();
if (m_size >= m_max_size) {
cond.broadcast();
m_mutex.unlock();
return false;
}
m_back = (m_back + 1) % m_max_size;
m_array[m_back] = item;
m_size++;
cond.broadcast();
m_mutex.unlock();
return true;
}
/*调用pop的是消费者,负责把生产者的内容写入文件*/
bool pop(T& item) {
m_mutex.lock();
while (m_size <= 0) {
if (!cond.wait(m_mutex.get())) {
m_mutex.unlock();
return false;
}
}
m_front = (m_front + 1) % m_max_size;
item = m_array[m_front];
m_size--;
m_mutex.unlock();
return true;
}
bool pop(T& item,int ms_timeout) {
struct timespec t = {0,0};//tv_sec :从1970年1月1日 0点到现在的秒数 tv_nsec:tv_sec后面的纳秒数
struct timeval now = {0,0};//tv_sec: 从1970年1月1日 0点到现在的秒数 tu_usec:tv_sec后面的微妙数
gettimeofday(&now,nullptr);
m_mutex.lock();
if (m_size <= 0) {
t.tv_sec = now.tv_sec + ms_timeout/1000;
t.tv_nsec = (ms_timeout % 1000) * 1000;
if (!m_cond.timewait(m_mutex.get(), t)) {
m_mutex.unlock();
return false;
}
}
//TODO:这一块代码的意义不知道在哪里,留着DEBUG
if (m_size <= 0) {
m_mutex.unlock();
return false;
}
m_front = (m_front + 1) % m_max_size;
item = m_array[m_front];
m_size--;
m_mutex.unlock();
return true;
}
private:
locker m_mutex;
cond m_cond;
T* m_array;
int m_max_size;
int m_size;
int m_front;
int m_back;
};
#endif