写webserver项目过程中遇到的关于多线程并发的问题

Buffer类的接口如下,可以看到,该类是线程安全的,支持多个线程向Buffer中写入数据而不会发生Race Condition。

//Buffer.h
#ifndef BUFFER_H
#define BUFFER_H
#include <string>
#include <algorithm>
#include "common.h"
#include <mutex>
class Buffer{

private:
    std::string buff_;//buff数据
public:
    std::mutex mut_;//用于保护缓冲区
    void append(char *data,int size);//往buffer中添加数据
    int size() ;//返回buff大小
    const char *ptr();//返回首地址指针
    void clear();
    void pop(int size);
    bool getMsg(std::string &msg);

};

#endif
//Buffer.cpp

#include "Buffer.h"


//往buffer中添加数据
void Buffer::append(char *data,int size){
    {
        std::lock_guard<std::mutex> guard(mut_);
        buff_.append(data,size);
    }
}
//返回buff大小
int Buffer::size() {
    std::lock_guard<std::mutex> guard(mut_);
    return buff_.size();
}
//返回首地址指针
const char *Buffer::ptr(){
    std::lock_guard<std::mutex> guard(mut_);
    return buff_.data();
}

void Buffer::clear(){
    std::lock_guard<std::mutex> guard(mut_);
    buff_.clear();
}

void Buffer::pop(int size){
    std::lock_guard<std::mutex> guard(mut_);
    buff_.erase(0,size);
}

bool Buffer::getMsg(std::string &msg){
    std::lock_guard<std::mutex> guard(mut_);
    if(buff_.size()>=4){
        int len;
        memcpy(&len,buff_.data(),sizeof(int));
        if(buff_.size()>=4+len){
            msg = std::string(buff_.data()+4,len);
            buff_.erase(0,4+len);
            return true;
        }
    }
    return false;
}

下面展示的是用于处理客户端消息完毕,相应客户端时的操作。由于Reactor模型中关于具体业务处理与对客户的IO操作要分离,即可能有多个业务线程处理完业务响应客户时,调用writeOutBuffoutBuff_ 中加入数据,而IO线程则会根据**outBuff_**中是否存在数据,将数据发送给客户端。

//TCPSERVER 类往输出缓冲区中添加数据
void Connection::writeOutBuff(std::string &data){

    char buff_[128];
    bzero(buff_,sizeof(buff_));
    int len = data.size();
    if(len<=0) //没有数据需要加入输出缓冲区
        return;
    memcpy(buff_,&len,sizeof(int));
    memcpy(buff_+sizeof(int),data.data(),len);
    outBuff_.append(buff_,len+4);
    if((connectionChannel_->events()&EPOLLOUT)==false)//若epoll未监听写事件
        connectionChannel_->enableWriting();//让epollloop监听写事件
}

//Channel类监听到写事件,回调Connection发送数据
void Connection::onMessageOut(){
    if(outBuff_.size()>0){
        int num = send(getCliSocket()->fd(),outBuff_.ptr(),outBuff_.size(),0);//将outBuff_中的数据尽可能多的发送出去
        if(num>0){
            outBuff_.pop(num);
        }
    }else{//输出缓冲区发送完毕,取消监听写事件
        sendCompleteCallBack_(this);
        //这里有问题,先不管
        //多线程调用writeOutBuff函数时可能刚刚启用监听写事件,这里马上就禁用写事件
        //特别是在数据发送末尾阶段可能发生,导致outBuff_中还有数据未发送就被禁用写事件
        //即outBuff_.size()==0 与下面这个操作不能一气呵成导致的
        connectionChannel_->disableWriting();
    }
}

问题出在这里onMessageOut这里,当IO线程检测到outBuff_ 中没有数据时,而业务线程刚好调用writeOutBuffoutBuff_ 中加入数据,这个时候 oubBuff_ 还存在数据,却直接被禁用写事件了,而且往往发生业务线程最后一次调用 writeOutBuff 时。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值