WebServer异步日志模块,Log类构析导致线程死锁解决分析

测试异步日志发现程序一直卡着,调了半天才发现感觉可能是线程死锁了,gdb查了两个线程bt果然是,主线程调用delete m_queue时要构析条件变量, 然而另一个线程已经用条件变量wait拿到互斥锁阻塞自己没法退出,导致主线程得一直等待。

把~Log的delete m_queue一行删除就解决了,但想尝试做到既能内存0泄露又能正常退出程序, 问题是不知道怎么解决工作线程的wait状态。

一开始以为在~block_queue部分加入m_cond.broadcast()就能解除工作线程wait状态了,毕竟是主程序调用的~block_queue, 然而不知道为何仍旧死锁。

热心的群友们有何良方吗?

log.h

#ifndef LOG_H
#define LOG_H
#include<iostream>
#include<string>
#include<cstring>
#include<stdarg.h>
#include<time.h>
#include<sys/time.h>
#include<queue>
#include<assert.h>

#include "../lock/locker.h"
#include "../log/block_queue.h"

using namespace std;
class Log{
    public:
        Log();
        ~Log();
        void init(const char *file_path, int log_buf_size = 8192, int max_line = 80000, int queue_size = 800);
        static Log* get_instance(){
            static Log instance;
            return &instance;
        }
        static void* async_write_thread(void *args){
            Log::get_instance()->async_write_log();
            return 0;
        };
        void write_log(int level, const char *format,...);
        void flush() {
            m_mutex.lock();
            fflush(m_fp);
            m_mutex.unlock();
        };
    private:
        char *get_current_time(char *time_buf, const char *format);
        void async_write_log(){
            string logstr;
            while(m_is_async && m_block_queue->pop(logstr)){
                m_mutex.lock();
                if(DEBUG) printf("log puts\n");
                fputs(logstr.c_str(),m_fp);
                m_mutex.unlock();
            }
            if(DEBUG) printf("thread end\n");
        };
    private:
        char m_log_dir[128];    //日志存放目录
        char m_log_name[128];   //日志名
        FILE *m_fp;
        bool m_is_async;        //异步日志模式,控制异步线程
        int m_today;
        int m_max_line;
        int m_line_cnt;
        int m_wlog_buf_size;
        char *m_wlog_buf;
        locker m_mutex;
        block_queue<string> *m_block_queue;
};

#define LOG_DEBUG(format, ...)  {Log::get_instance()->write_log(0,format,##__VA_ARGS__); Log::get_instance()->flush();}
#define LOG_INFO(format,...) {Log::get_instance()->write_log(1,format,##__VA_ARGS__); Log::get_instance()->flush();}
#define LOG_WARN(format, ...)  {Log::get_instance()->write_log(2,format,##__VA_ARGS__); Log::get_instance()->flush();}
#define LOG_ERROR(format, ...) {Log::get_instance()->write_log(3,format,##__VA_ARGS__); Log::get_instance()->flush();}

#endif  //LOG_H

locker.h

#ifndef LOCKER_H
#define LOCKER_H

#include<exception>
#include<pthread.h>
#include<semaphore.h>
class sem{
    public:
        sem(){
            if(sem_init(&m_sem, 0, 0) != 0){
                throw std::exception();
            }
        }
        bool wait(){
            return sem_wait(&m_sem) == 0;
        }
        bool post(){
            return sem_post(&m_sem) == 0;
        }
        ~sem(){
            sem_destroy(&m_sem);
        }
    private:
        sem_t m_sem;
};
class locker{
    public:
        locker(){
            if(0 != pthread_mutex_init(&m_mutex, NULL)){
                throw std::exception();
            }
        }
        pthread_mutex_t *get(){
            return &m_mutex;
        }
        bool lock(){
            return pthread_mutex_lock(&m_mutex) == 0; 
        }
        bool unlock(){
            return pthread_mutex_unlock(&m_mutex) == 0;
        }
        ~locker(){
            pthread_mutex_destroy(&m_mutex);
        }
    private:
        pthread_mutex_t m_mutex;
};
class cond{
    public:
        cond(){
            if(0 != pthread_cond_init(&m_cond,NULL)){
                throw std::exception();
            }
        }
        ~cond(){
            pthread_cond_destroy(&m_cond);
        }
        bool wait(pthread_mutex_t *m_mutex){
            int ret = pthread_cond_wait(&m_cond,m_mutex);
            return ret == 0;
        }
        bool signal(){
            int ret = pthread_cond_signal(&m_cond);
            return ret == 0;
        }
        bool broadcast(){
            int ret = pthread_cond_broadcast(&m_cond);
            return ret == 0;
        }

    private:
        pthread_cond_t m_cond;
        
};
#endif

block_queue.h

#ifndef BLOCK_QUEUE
#define BLOCK_QUEUE
#include<queue>
#include "../lock/locker.h"
template <typename T>
class block_queue{
    public:
        block_queue(int sz):m_maxsize(sz){}
        //往阻塞队列加入日志string
        bool push(const T &items){
            m_mutex.lock();
            //如果队列已满,需通知消费者
            if(m_queue.size() >= m_maxsize){
                m_cond.broadcast();
                m_mutex.unlock();
                return false;
            }      
            m_queue.push(items);
            m_cond.broadcast();
            m_mutex.unlock();
            return true;
        }
        bool pop(T &items){
            m_mutex.lock();
            //若队列为空,需等待条件变量
            while(m_queue.size() <= 0){
                if(!m_cond.wait(m_mutex.get())){
                    m_mutex.unlock();
                    return false;
                }
            }
            items = m_queue.front();
            m_queue.pop();
            m_mutex.unlock();
            return true;
        }
        bool full(){
            m_mutex.lock();
            if (m_queue.size() >= m_maxsize){
                m_mutex.unlock();
                return true;
            }
            m_mutex.unlock();
            return false;
        }
        bool empty(){
            m_mutex.lock();
            if(m_queue.size() == 0){
                m_mutex.unlock();
                return true;
            }
            m_mutex.unlock();
            return false;
        }
        ~block_queue(){
            m_cond.broadcast();
        }
    private: 
        int m_maxsize;
        locker m_mutex;
        cond m_cond;
        std::queue<T> m_queue;
};

#endif 

testlog.cpp

#include"log.h"
#include<unistd.h>
int main(){
    Log::get_instance()->init("testfile");
    LOG_INFO("This is a test message:)");
    sleep(3);
    const char *mes = "log ends";
    write(1,mes,strlen(mes));
    printf("log ends\n");
    return 0;
}

现象:

image-20220930192323938

后继

一番研究后找到一个解决思路, 首先日志类已经设了一个m_is_async的bool变量用于打破工作线程的循环,而pop条件变量的解锁条件是阻塞队列内有push东西经历,于是在~Log放了一行操作让m_queuepush东西打破工作线程的阻塞状态退出,工作线程再次while检查就会发现m_is_async已经被设为false了,从而正常退出。

//Log类
Log::Log(){
    m_line_cnt = 0;
    m_is_async = true;
    memset(m_log_dir,0,sizeof(m_log_dir));
    memset(m_log_name,0,sizeof(m_log_name));
}

Log::~Log(){
    if (m_fp != NULL){
        fclose(m_fp);
    }
    m_is_async = false;
    delete[] m_wlog_buf;
    m_block_queue->push("log end"); //用于结束日志线程的阻塞状态,不加会死锁
    delete m_block_queue;
    
}

void async_write_log(){
    string logstr;
    while(m_is_async && m_block_queue->pop(logstr)){
        m_mutex.lock();
        fputs(logstr.c_str(),m_fp);
        m_mutex.unlock();
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值