Socket套接字在多线程发送数据时要加锁吗?

转自知乎

 

问题:一条报文在1000字节到4000字节之间,数据量在每秒5000左右,多线程写套接字,要加锁吗?没有加锁会导致报文错乱吗?在网络编程的时候,报文不超过MTU是否就无需对套接字加锁?


解答①:
你连 UDP 还是 TCP 都没说。


对于 UDP,多线程读写同一个 socket 不用加锁,不过更好的做法是每个线程有自己的 socket,避免 contention,可以用 SO_REUSEPORT 来实现这一点。
对于 TCP,通常多线程读写同一个 socket 是错误的设计,因为有 short write 的可能。假如你加锁,而又发生 short write,你是不是要一直等到整条消息发送完才解锁(无论阻塞IO还是非阻塞IO)?如果这样,你的临界区长度由对方什么时候接收数据来决定,一个慢的 peer 就把你的程序搞死了。


总结:对于 UDP,加锁是多余的;对于 TCP,加锁是错误的。


作者:陈硕
链接:https://www.zhihu.com/question/56899596/answer/150926723
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


解答②:
已经有陈硕大神镇楼了,腆颜说几句。很多网络库和框架其实已经向我们提供了最佳实践,应用中的所有socket都应该是异步非阻塞且只在一个事件循环(一般还会一个线程只有一个事件循环)中进行数据io,多线程io一个socket加不加锁都是非主流的做法


解答③:
对于TCP,正确的做法是,创建一个单独的发送线程,让发送线程统一发送数据。例如你的工作线程池处理完了业务task,你把需要回复的包封装成一个发送task,包含发送套接字和消息,丢给发送线程就行了。


对于UDP,它是面向报文的是一个完整的报文接一个完整的报文的,不需要加锁,在UNP中也提到过一个发送就是一个原子操作。其实我们从UDP的接收都可以看的出来,UDP接收的时候需要设置接收缓冲大于报文大小,并且每次从接收缓冲都是只能取回一个完整的包,不需要处理粘包,半包。首先是说了UDP协议栈会在发送的时候分片,到了接收重组,每次组好了完整的包就丢给用户,不完整就丢掉。其次说明了协议栈是以报文整个的形式来传递,所以是原子操作,“线程安全”。


作者:coding man
链接:https://www.zhihu.com/question/56899596/answer/248122959
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

如果需要多个线程同时读访问或写访问同一个tcp socket, 那多半是程序设计上出了问题。
对于tcp的传输,都是要用应用层的协议保证接收方能解释对方发送过来的数据,这样,至少要保证一块数据是一个完整的协
议包。
如果是多个线程同时写socket,那么在临界区里工作的线程为了保证数据包的完整发送,必须循环发送等待,直到数据都发
送到系统缓冲区才能离开临界区。而这样就导致socket的行为与阻塞方式发送无异,发送效率因此而被限制。
我觉得一个好的tcp socket处理框架,应该是只有一个线程来负责数据的收发,从而避免那些无穷无尽的同步问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值