如果你偶然浏览到这里,请先看 C/S框架 st_asio_wrapper 开发教程(一)
源代码及例程下载地址:
git:https://github.com/youngwolf-project/st_asio_wrapper/。
QQ交流群:198941541
九:陷阱
大家都知道多线程死锁,进程间死锁,今天我要说的是,两台网络通信中的电脑,也会死锁,不可思议吧?那么st_asip_wrapper会死锁吗?请往下看。
先举个网络编程中死锁的典型例子,假如AB两个程序做网络通信(谁是服务端谁是客户端无所谓),都采用单线程阻塞模式,假设在某个时刻,A因发送缓存满而被阻塞在send,此时B(AB在不同电脑上,所以理论上,他们完全不相干涉,换句话说,无论A处于什么业务状态, B都有可能处于任意一个业务状态)完全有可能也因为发送缓存满而被阻塞在send,由于A被阻塞在send,说明B的接收缓存满了(不满的话,数据总是会从A到达B的,数据的接收是由网络驱动自动做的,不管我们是否接收数据,它总是会在可能的情况下尽量接收数据到本地缓存),由于B被阻塞在send,说明A的接收缓存也满了(原理同前面所说)。两个套接字的四个缓存都满!此时,死锁就发生了,要想让A从send返回,B必须从自己的接收缓存里面读取一些数据,以便让A发送一些数据到B。可是不幸的是,B被阻塞在send,它没机会执行recv来读取一些数据。反之已然。
那么大家为什么很少遇到上面的情况呢,因为四个缓存都满,需要一些特定的条件,就是两边的发送一定要非常快,不一定要一直快,当死锁一但发生,马上把速度降下来也没用了(其实根本就没速度了,因为卡住了),死锁无法自解。如果你对你的产品做压力测试,相信可能会遇得上的。
那么st_asio_wrapper是否会死锁呢?如果你因为等待发送缓存可用而阻塞在on_msg_handle,死锁是有可能发生的,没错,多线程异步IO居然要死锁,其原因是消息处理(on_msg_handle)被阻塞,一但数据接收被停止,那么情况和单线程阻塞模式是一样的。
注意,只要不是因为等待发送缓存可用而阻塞在on_msg_handle(两端都这样),都是没有问题的,大家也不要太害怕,比如你阻塞在on_msg_handle里面处理自己的业务,或者阻塞在其它地方,比如你自己的线程都是没有问题的。换句话说,只要不阻塞在service线程里面,或者即便阻塞在service线程,但解除阻塞的条件与本socket的发送缓存可用性无关,就不会造成死锁,只会有限的阻塞,死锁是无限的阻塞。
更方便简洁但有些暴力的方法是,以can_overflow为true调用send_msg(相当于发送缓存永远可用),这个就需要开发者保证缓存不会达到不可控的直线上升状态,也就是总要有让缓存减少的机制,或者业务本身在达到某个高度之后,就会下降,这些情况下,都可以让can_overflow为true。
那么到底要怎样做拥塞控制呢,具体请参看教程第五篇。
十:线程安全性
默认情况下service_pump开启8个IO线程,只要线程数量多于1个,那么所有的回调方法(以on开关的虚函数),都是并发的(除非是明显有顺序关系的,且在同一个socket对象上的时候),具体说来(以on_msg_handle和on_msg_send为例):对于同一个socket,on_msg_handle和on_msg_send是并发的,on_msg_handle和on_msg_handle(下一个)、on_msg_send和on_msg_send(下一个)是顺序的;对于不同的socket,所有回调都是并发的。
关于定时器回调的并发性,不同timer之间当然是并发,同一个timer对象的同一个timer(以id区分timer)回调是顺序,不同的timer的回调是并发的。注意:多线程对同一个timer对象调用set_timer设置同一个timer(id相同)是不安全的,设计即是这样(否则每一个timer对象里面的每一个timer都需要一个互斥对象)。
其它的方法,除了明显不应该设计为多线程的(比如初始化,开始结束服务等),都是线程安全的,比如send_msg等。
十一:关于文件传输工具的使用
这是我写的两个基于st_asio_wrapper的demo,file_server和file_client,支持简单的聊天和文件分块传输,在局域网里面,最好别分块,因为瓶颈在磁盘IO,如果从互联网上传输,则分块肯定可以提高速度,就像迅雷一样。
使用方法很简单,把要传输的文件放到file_server能访问到的地方,开启file_server。file_client的运行方式是:file_client link_num,其中每一条连接代表一个文件块,如果要分5块同时下载,则link_num设为5,当file_client所有连接都成功连到服务器时,输入命令:get file_name1 file_name2 ...,开始以次传输每一个文件。输入其它任何内容则当成聊天信息直接发送。如果传送的文件不在当前路径,比如 get ../123/a.txt,则要求客户端也必须存在../123这个目录,这个如果你觉得是问题的话,可以修改demo。
st_asio_wrapper使用FAQ
C/S框架 st_asio_wrapper 开发教程(五)