我在发送数据时最初有一个竞争条件,问题是我允许使用多个SocketAsyncEventArg来发送数据,但第一个数据包在第二个数据包之前没有完全发送,这是因为我有它,所以如果数据不适合缓冲区,它会循环,直到所有数据都发送完,因此第二个数据包在第一个数据包之前被发送并到达客户端。
我已经解决了这个问题,将1个SocketAyncEventArgs分配给一个用于发送数据的开放连接,并使用Semaphore来限制对它的访问,并在完成后进行SocketAsyncEventArg回调。
现在,这一切都很好,因为所有数据都已发送,当它完成时会回调,为下一次发送做好准备。这个问题是,当我想随机向打开的连接发送数据时,它会导致阻塞,当有很多数据发送时,它就会阻塞我的线程。
我正在寻找解决方案,我想到了一个队列,当请求发送数据时,它只需将数据包添加到队列中,然后1个SocketAsyncEventArgs只需循环发送数据。
但是,我如何在保持可扩展性的同时高效地做到这一点呢?我想在按照要求发送数据包的顺序发送数据包时尽可能避免阻塞。
感谢您的帮助!
如果数据需要保持有序,并且您不想阻塞,那么您需要添加一个队列。我这样做的方法是在我的状态对象上跟踪我们是否已经为该连接有一个活动的发送异步循环。入队后(显然必须同步),只需检查正在进行的内容:
public void PromptToSend(NetContext context)
{
if(Interlocked.CompareExchange(ref writerCount, 1, 0) == 0)
{ // then **we** are the writer
context.Handler.StartSending(this);
}
}
这里writerCount
是连接上写循环的计数(应该正好是1或0);如果没有,我们就开始一个。
我的StartSending
尝试从该连接的队列中读取;如果它能做到这一点,它会执行通常的SendAsync
等:
if (!connection.Socket.SendAsync(args)) SendCompleted(args);
(请注意,这里的SendCompleted
用于"sync"情况;它将通过"async"情况的事件模型到达SendCompleted
)。显然,SendCompleted
重复了"出队列,尝试发送异步"的步骤。
剩下的唯一一件事是确保当我们尝试出列时,如果我们没有其他事情可做,我们会注意到缺乏行动:
if (bufferedLength == 0)
{ // nothing to do; report this worker as inactive
Interlocked.Exchange(ref writerCount, 0);
return 0;
}
有道理吗?