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();
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在进行计算机网络 Web 服务器课程设计的需求分析时,需要清楚了解以下几个方面: 1. 功能需求:明确服务器需要实现的功能,例如:接收客户端请求、路由处理、静态资源服务、动态资源服务、身份验证、日志记录等。 2. 性能需求:根据实际情况,确定服务器需要支持的并发请求数、响应时间、吞吐量等性能指标。 3. 安全需求:明确服务器需要实现的安全措施,例如:身份验证、密码加密、防止恶意攻击等。 4. 可用性需求:确定服务器需要具备的可用性指标,例如:可靠性、可维护性、可扩展性等。 5. 用户界面:如果需要提供用户界面,需要明确用户界面需要提供的功能和操作方式。 需求分析是计算机网络 Web 服务器课程设计的重要步骤,能够帮助我们明确服务器的设计目标和实现路径,提高服务器的质量和性能。 ### 回答2: 计算机网络web服务器课程设计需求分析主要涉及以下几个方面。 首先,课程设计需要明确目标。设计者需要了解课程的目标是什么,是为了培养学生对web服务器相关知识的理解和应用能力,还是为了让学生能够独立设计和开发一个简单的web服务器。明确目标有助于设计者确定课程内容和教学方法。 其次,课程设计需要考虑到学生的基础知识和能力。设计者需要了解学生在计算机网络和web技术方面的基础知识和能力,以便合理安排课程内容和难度。例如,如果学生已经具备了一定的网络编程和web开发的基础,可以设计更高级的课程。 另外,课程设计需要结合实践环节。学生需要通过实践来巩固所学的知识和技能。设计者可以设置实践项目,让学生通过设计和开发一个简单的web服务器来应用所学的知识。通过实践,学生可以更深入地理解和运用课程内容。 此外,课程设计还需要考虑到教学资源和环境的准备。设计者需要确定教材和参考资料,并提供必要的实验设施和开发工具给学生使用。同时,还需要制定评价方法,以便对学生的学习情况进行评估。 总之,计算机网络web服务器课程设计需要明确目标、考虑学生基础知识和能力、结合实践和准备教学资源和环境。只有全面考虑这些因素,才能设计出适合学生学习的课程。 ### 回答3: 计算机网络webserver课程设计需求分析主要包括以下几个方面: 1. 功能需求:首先,设计一个能够支持基本的HTTP协议的webserver,能够接收客户端发出的请求,并返回相应的数据。同时,webserver还应该支持动态网页的生成与处理,能够解析PHP、ASP等脚本语言,并根据客户端请求动态生成页面内容。此外,webserver还可以支持静态网页的访问,能够处理HTML、CSS、JavaScript等语言。 2. 性能需求:在设计webserver时,需要考虑其性能要求。即使在高并发的情况下,webserver仍需要能够保持较高的响应速度,以便迅速处理客户端的请求。为了提高性能,可以使用多线程或多进程的方式处理多个客户端请求,充分利用计算机的多核处理能力。 3. 安全需求:webserver设计时需要考虑安全性,以防止恶意攻击和非法访问。可以通过设置访问权限,禁止非法访问资源。此外,还可以应用加密技术,确保传输的数据在网络中的安全性。 4. 可扩展性需求:设计时应考虑webserver的可扩展性,以便能够满足未来的需求变化。对于新增功能的需求,如数据库访问、文件上传等功能,设计时应留有扩展接口,方便后续的功能扩展和接入。 5. 可维护性需求:设计一个易于维护的webserver,以方便对其进行升级和修复。可以使用模块化的设计思想,将webserver分为多个模块,各个模块之间相互独立,方便修改和维护。同时,为了方便日志的记录和故障排查,webserver还应支持日志功能,记录访问日志、错误日志等信息。 综上所述,计算机网络webserver课程设计需求分析包括功能需求、性能需求、安全需求、可扩展性需求和可维护性需求等方面。这些需求将为设计师提供指导,确保最终设计的webserver能够满足用户的需求,具备良好的性能和安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值