云计算百科
云计算领域专业知识百科平台

使用事件循环管理socket,处理大文件传输

本次项目为Qt聊天室第三次开发的经验总结

项目地址:

https://gitee.com/how-to-name/qt—simple-online-chat-room GitHub – RH211-sys/qt—simple-online-chat-room · GitHub

 

快速理解事件循环

可以借助epoll的执行过程来快速理解事件循环机制,其实就是如下:

  • 拥有事件循环这一属性的线程先进入阻塞态

  • 当事件发生时(网络数据到达,超时等),唤醒该线程

  • 处理事件:调用对应的处理函数

  • 处理完成后进入阻塞态,回到步骤1

  • 事件发生后,内核把"发生事件的对象"整理成一张就绪列表直接交给了线程——线程醒来直接读这张列表,不需要回容器里逐个找哪个对象发生了事件

     

    事件循环对socket管理

    如何良好的管理socket在网络项目中是一个非常重要的事情,好的管理方式往往能提高开发效率,提高可维护性

    对socket管理的解决方案反思

    在该Qt项目中,我前后尝试了三个阶段的解决方案

  • 所有线程都能对socket进行读写

  • 所有线程都能对socket进行加锁读写

  • 主线程对socket读写(网络IO),其他线程进行本地IO和其他操作

  • 方案1的思考来源:为了能快速的处理网络IO,想着使用多线程处理网络数据。

    缺陷:容易出现竞态,会出现多个线程同时对同一个socket读/写的情况,且后续了解到Qt不推荐跨线程直接操作socket对象

     

    方案2的思考来源:为了避免竞态,只能牺牲一些性能,加锁来同步处理网络数据

    缺陷:频繁的消息转发/文件传输,容易导致锁的开销比较大,且后续了解到Qt不推荐跨线程直接操作socket对象,尽管保证了同步

     

    方案3的思考来源:减少锁开销,用主线程处理网络IO

    优势:实现网络IO和本地IO的解耦,且性能比方案2提升很大 潜在缺陷:单线程管理可能不支持大量客户端的管理

    更好的方案(更适合大体量的网络项目规模)

    使用IO线程组对socket分组管理(每个线程负载均匀的socket)

    可以简单理解为如下模型:

     用IO线程组管理socket
     ​
     任务队列所存储的任务类型:
      1. 新增socket(按最少连接数分配到线程)
      2. 删除socket
      3. 跨线程写请求(其他线程要往某个socket写数据时,投递"数据 + 目标socket",由该socket所属线程执行write/flush)
     
     线程池中的每个线程:
      每个线程单独用epoll去管理socket,线程之间的socket集不重叠
      事件发生:
      1. readReady: 线程对发生事件的socket进行读操作
      2. 其他线程发来往某个socket的写操作请求:线程对发生事件的socket进行写操作,并flush
      3. xxxx
       
       
     事件处理完后(没有任务),进入阻塞态,如此循环

     

    这其实也是多 Reactor 多线程模型。

    理解了这个后,对多线程/线程池模型的认识会更加清晰

    实际项目中的情况是: 主线程统一管理大部分网络 I/O,聊天被单独派到子线程A 读写(唯一例外,锁保护)

    因为聊天消息内容很少,且性能影响不高,故无需过多考虑,就没有进行太多改动

     

     

    线程池落盘原理(生产-消费队列)

    本次项目的线程池用于处理网络传输的文件,把客户端传来的文件写到服务端的磁盘中

    我使用了两个任务队列

    队列A(线程池任务队列):客户端开始传输文件时先发 header(文件名、大小等),主线程解析完 header 后创建一个落盘任务,提交进队列A。

    队列B(每个任务自带的数据队列):之后客户端只发数据,主线程每次从 socket 读出数据块,追加进该任务自己的队列B。

    线程池分配一个空闲线程执行该任务:调用run() 进入循环:等自己的队列B有数据 → 取块写盘; 主线程读到结束标志后调 finish() 通知 → 线程写完队列剩余数据、结束任务(回调主线程收尾)→ 线程回到池中空闲,等待队列A分配下一个任务。

     

     

     

     

     

     

     

     

     

     

     

     

     

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 使用事件循环管理socket,处理大文件传输
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!