关于并发与多线程处理与缓冲区

读与写

  • read:只能监控一个句柄
  • select:一次性监控多个句柄是否可读/可写/异常,超时退出。
    • 原理:所有句柄进入等待队列,只要有一个句柄的状态改变,立刻返回(进程从阻塞状态进入就绪),再遍历所有句柄,找到状态发生改变的句柄
    • 文件句柄有限:1024个
    • 文件句柄是最大值+1
    • 超时时间必须每次都初始化(否则,这个时间是剩余的时间)
    • 可以一次性不同类型的文件句柄,设备文件/socket/管道等等
    • 内部使用轮询来检查状态是否改变
  • poll:同select
    • 区别:不是使用fd_set,而是使用链表,这样就没有句柄数量限制了
  • epoll:和select类似
    • 区别:
    • 原理:所有句柄进入等待队列,只要有一个句柄的状态改变,把这个句柄放入eventpoll中的就绪队列(直接知道是哪个改变了,不用像select一样进行辩遍历)。进程再从阻塞状态进入就绪,再进行处理。进程处理完再进入eventpoll中的就绪队列继续阻塞。
    • 水平触发:事件触发后,句柄就放在就绪队列,处理完了再放回等待队列
    • 边缘触发:区别:没处理完也放回等待队列

  • write

多线程性能提升

  • 多线程
    • read到数据后,将数据处理放入线程中进行处理
    • 使用线程池,从线程池中获取,再放回线程池,避免重复的创建与释放
    • 再提提速:把read和write也放入线程中(虽然是读写缓存,但用户空间和内核空间的复制也是消耗一定时间),把这段时间空闲出来进行监控句柄的变化
    • 能不用锁就不用锁
    • 注意锁的粒度,尽量小。原子锁(最小粒子)>互斥锁(unique_lock(自动加锁/解锁)>lock_guard>mutex)>读写锁(读写次数差异大)>自旋锁(实时性好)
  • 信号量和条件变量

缓冲区

  • 拆包
    • 发送方缓冲区太小,一次发不完
    • 接收方缓冲区太小,一次收不完
  • 粘包
    • 发送方缓冲区太大,发送太快
    • 接收方缓冲区太大,没有及时读取
    • 解决:定义消息边界,发送方发head+body,接收方根据head确定读几个字节

代码

/**
 * @description: select
 * @param {type} 
 * @return: 
 */
int readDevice(char *buff, int buffSize)
{
    int len, fs_sel;
    fd_set fs_read;
    struct timeval time;

    if (fd <= 0 || NULL == buff)
    {
        return -1;
    }

    time.tv_sec = 1; // 1 sec
    time.tv_usec = 0;

    FD_ZERO(&fs_read);
    FD_SET(fd, &fs_read);
    // 只监控是否可读
    fs_sel = select(fd + 1, &fs_read, NULL, NULL, &time);
    if (fs_sel > 0)
    {
        len = read(fd, buff, buffSize);
        printf("len = %d fs_sel = %d\n", len, fs_sel);
        return len;
    }
    else
    {
        printf("can not read\n");
    }

    return len;
}

#include <sys/epoll.h>

int epid; // epoll标识符
int max_value = 5;
struct epoll_event event;
struct epoll_event events[6]; // 事件集合

/**
 * @description: epoll初始化
 * @param {type} 
 * @return {type} 
 */
int init_epoll(int fd)
{
    // 监听多个
    epid = epoll_create(max_value);

    // 边缘触发 可读 只监听一次
    event.events = EPOLLET | EPOLLIN | EPOLLONESHOT;
    event.data.fd = fd;

    // 事件注册
    if (epoll_ctl(epid, EPOLL_CTL_ADD, fd, &event) != 0)
    { // 将读事件添加到epoll的事件队列中
        printf("set epoll error!\n");
        return 0;
    }
    printf("set epoll ok!\n");

    return 1;
}

/**
 * @description: epoll
 * @param {type} 
 * @return {type} 
 */
int readDevice2(char *buff, int buffSize)
{
    int ret = 0;
    int len = 0;

    // 等待事件
    ret = epoll_wait(epid, events, max_value, 1000); // 1000ms
    if (ret == 0)
    {
        return 0;
    }

    for (int i = 0; i < ret; i++)
    {
        if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP) || (!(events[i].events & EPOLLIN)))
        {
            printf("no data!\n");
            break;
        }
        else if (events[i].events & EPOLLIN) // 可读
        {
            len = read(events[i].data.fd, buff, buffSize);
        }
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值