+ -
当前位置:首页 → 问答吧 → 多线程影响异步Socket服务器接收致网络流量下降

多线程影响异步Socket服务器接收致网络流量下降

时间:2011-12-28

来源:互联网

现象描述:
1、使用SocketAsyncEventArgs建立异步Socket服务器,接收从客户端发来的数据
2、与网络无关的数据处理线程的某些操作,貌似对Socket接收线程造成影响,导致网络接收流量大幅下降,从而导致客户端发送队列阻塞
3、屏蔽该数据处理线程的某些操作,网络接收流量可达到99%
4、接收线程并没有做过多费时工作,只是将数据压入某队列,然后继续调用接收下一包

问题
异步Socket的数据接收线程是从系统线程池中取得,是否存在与其他数据处理线程的资源争用(比如线程的开启关闭调度等),导致内核级的开销,使Socket接收线程受到影响

作者: bei0305   发布时间: 2011-12-28

看不到你的代码,我想知道你统计发送和接收的包的数量,是通过什么方式?如果是发送了n次,接收到m次这样来统计,那么就是多次发送的数据包被一次合并接收了。如果不是这种方式,请你说明一下统计方式。
Socket要做到准确接收,不分包是不可能的,至少要在数据包的头部定义长度。

作者: lizhibin11   发布时间: 2011-12-28

引用 1 楼 lizhibin11 的回复:

看不到你的代码,我想知道你统计发送和接收的包的数量,是通过什么方式?如果是发送了n次,接收到m次这样来统计,那么就是多次发送的数据包被一次合并接收了。如果不是这种方式,请你说明一下统计方式。
Socket要做到准确接收,不分包是不可能的,至少要在数据包的头部定义长度。

我主要是接收大量数据,理想情况下网卡接收流量在99%,有分包的,定义有包头,包长度等信息,接收只负责接收,保存入队列,另外有线程来分包解析的

作者: bei0305   发布时间: 2011-12-28

哦,是这样,我对99%是什么理解错了,不好意思。
接收到数据后的回调操作是在线程池里,你的处理队列的线程我不知道是通过什么方式,是一直有一个线程在轮循队列,还是往队列里添加新数据时判断队列是否为空,为空则启动新的线程来处理。
如果担心线程调度会影响性能,使用BeginInvoke也是在调用系统线程池的线程进行处理。
不过我觉得更好的方式是不主动使用新线程来处理队列。回调后的操作本身就在线程池,那么接收到数据后,直接进行下一次接收,然后在本线程判断队列是否为空,为空则处理,不为空则仅仅添加(队列每处理完一个包后检测队列是否为空,不为空则继续处理)。这样并不影响处理之前进行的下一次接收回调时使用线程池里面的其他线程。

作者: lizhibin11   发布时间: 2011-12-28

引用 3 楼 lizhibin11 的回复:

哦,是这样,我对99%是什么理解错了,不好意思。
接收到数据后的回调操作是在线程池里,你的处理队列的线程我不知道是通过什么方式,是一直有一个线程在轮循队列,还是往队列里添加新数据时判断队列是否为空,为空则启动新的线程来处理。
如果担心线程调度会影响性能,使用BeginInvoke也是在调用系统线程池的线程进行处理。
不过我觉得更好的方式是不主动使用新线程来处理队列。回调后的操作本身就在线程……

我是这个样子处理的
接收的回调函数向一个队列里Push操作(lock队列),紧接着进行下一次接收
另外一个处理线程轮询队列Pop操作(lock队列),并解析处理数据(对于解出普通控制消息包直接回应,对于解出的数据包仍另外线程处理)
我所指的貌似影响接收的是另外一个数据包解析线程

原因是如果在接收线程里及解析处理数据会影响下一次接收
另外数据包解析耗费一定时间,仍另外线程处理

作者: bei0305   发布时间: 2011-12-28

引用 3 楼 lizhibin11 的回复:

哦,是这样,我对99%是什么理解错了,不好意思。
接收到数据后的回调操作是在线程池里,你的处理队列的线程我不知道是通过什么方式,是一直有一个线程在轮循队列,还是往队列里添加新数据时判断队列是否为空,为空则启动新的线程来处理。
如果担心线程调度会影响性能,使用BeginInvoke也是在调用系统线程池的线程进行处理。
不过我觉得更好的方式是不主动使用新线程来处理队列。回调后的操作本身就在线程……

因为我屏蔽掉数据包处理线程的部分代码后,网络流量正常,发送端不会出现发送缓冲区满的情况,但是这个数据包处理线程和网络接收线程是分开的,没有关系啊貌似

作者: bei0305   发布时间: 2011-12-28

ReceiveAsync回调函数(线程1)中,先不要处理接收到的数据,直接再次执行ReceiveAsync,然后向队列添加数据包(不开新线程,此操作还是在线程1),第二次Receive的回调函数会在线程池的另一个线程(线程2)中执行。这样的弊端就是接收到的数据包在队列中乱序,如果不是完整的包添加到队列中的,你需要重组。
如果先添加到队列,再执行下一次ReceiveAsync,就是你说的“如果在接收线程里及解析处理数据会影响下一次接收”。

作者: lizhibin11   发布时间: 2011-12-28

引用 6 楼 lizhibin11 的回复:

ReceiveAsync回调函数(线程1)中,先不要处理接收到的数据,直接再次执行ReceiveAsync,然后向队列添加数据包(不开新线程,此操作还是在线程1),第二次Receive的回调函数会在线程池的另一个线程(线程2)中执行。这样的弊端就是接收到的数据包在队列中乱序,如果不是完整的包添加到队列中的,你需要重组。
如果先添加到队列,再执行下一次ReceiveAsync,就是你说的“如果在……

多谢lizhibin11回复,你说的很对,但我做过测试,如果只做添加到队列,再执行下一次ReceiveAsync,另外一个线程一直循环Pop数据,是不会影响到网络接收的,也就是网络流量是在比较规律的区间内波动,比如50~100%

加上Pop以后的后续数据处理(将解析出的逻辑包的内容扔到另外的线程去处理)就会影响网络接收,网卡接收流量就会下降,从抓包看服务端会大量出现发送zero window包,但是后续的处理已经和Pop所在线程没有关系,怎么会影响到网络呢

作者: bei0305   发布时间: 2011-12-28

引用 6 楼 lizhibin11 的回复:

ReceiveAsync回调函数(线程1)中,先不要处理接收到的数据,直接再次执行ReceiveAsync,然后向队列添加数据包(不开新线程,此操作还是在线程1),第二次Receive的回调函数会在线程池的另一个线程(线程2)中执行。这样的弊端就是接收到的数据包在队列中乱序,如果不是完整的包添加到队列中的,你需要重组。
如果先添加到队列,再执行下一次ReceiveAsync,就是你说的“如果在……


我怀疑是不是因为,我所启动的线程与线程池中异步Socket回调线程发生了资源争用什么的,导致影响了接收线程从网卡取数据,并返回给上层

作者: bei0305   发布时间: 2011-12-28

如果处理队列和接收都是线程池中的线程,接收后的回调函数有可能会面临线程池中暂时无可用线程的情况,延缓下一次接收。如果处理队列的线程不在线程池中,可能会与线程池中的线程发生cpu争用(包括与其它进程的争用),同样延缓下一次接收。同样的资源,增加了处理的过程,发生争用也是正常的。
我前面说的其实是向队列添加数据包这个操作都要放到下一个receiveasync调用之后,而不是之前。当然这样同样也避免不了争用,毕竟多线程不能实质上解决资源问题。

作者: lizhibin11   发布时间: 2011-12-28