错误Sequence contains no elements
请使用.FirstOrDefault()
而不是.First()
否则 ==队列为空时== 会报错 Sequence contains no elements
解决线程冲突
由异步的机制可以知道,BeginSend
和回调函数往往执行于不同的线程,如果多个线程同时操作writeQueue
,有可能引发些问题。
在图4-36所示的流程中,玩家连续点击两次发送按钮,假如运气特别差,第二次发送时,第一次发送的回调函数刚好被调用。
如果线程1的Send刚好走到writeQueue.Enqueue(ba)这一行(t2时刻),按理说writeQueue.Count
应为2,不应该进入if(writeQueue.Count==1)
的真分支去发送数据(因为此时writeQueue.Count==2
)。
但假如在条件判断之前,回调线程刚好执行了writeQueue.Dequeue()
(t3时刻),由于writeQueue
里只有1个元素,在t4时刻主线程判断if(writeQueue.Count==1)
时,条件成立,会发送数据。
但SendCallback
中ba=writeQueue.First()
也会获取到队列的第一条数据,也会把它发送出去。第二次发送的数据将会被发送两次,显然不是我们需要的。
为了避免线程竞争,可以通过加锁(lock)的方式处理。当两个线程争夺一个锁的时候,一个线程等待,被阻止的那个锁变为可用。关于锁的介绍,读者可以去网上搜寻更多资料。加锁后,4.5.3节的代码如下:
1 | public void Send(BaseMessage info) |
高效的接收数据
1.Copy操作
要做到极致,那就极致到底。回顾4.3.4节中接收数据的代码(OnReceiveData),每次成功接收一条完整的数据后,程序会调用Array.Copy,将缓冲区的数据往前移动。但Array.Copy是个时间复杂度为 ==$O(n)$== 的操作,假如缓冲区中的数据很多,那移动全部数据将会花费较长的时间。