开源crm客户管理系统网站优化体验报告
### 循环队列和阻塞队列
#### 循环队列
 - **定义**: 一个固定大小的数组,通过两个指针(`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
