尝试通过BeginSend调用为队列发送消息似乎表现为阻塞调用。
特别是我有:
public void Send(MyMessage message)
{
lock(SEND_LOCK){
var state = ...
try {
log.Info("Begin Sending...");
socket.BeginSend(message.AsBytes(),0, message.ByteLength, SocketFlags.None,
(r) => EndSend(r), state);
log.Info("Begin Send Complete.");
}
catch (SocketException e) {
...
}
}
}
回调将是这样的:
private void EndSend(IAsyncResult result) {
log.Info("EndSend: Ending send.");
var state = (MySendState) result.AsyncState;
...
state.Socket.EndSend(result, out code);
log.Info("EndSend: Send ended.");
WaitUntilNewMessageInQueue();
SendNextMessage();
}
大部分时间这都很好,但有时会挂起。记录表明当相同的线程上的BeginSend和EndSend完成时会发生这种情况。 WaitUntilNewMessageInQueue 阻塞,直到队列中有新消息,因此当没有新消息时,它可以等待一段时间退出。
据我所知,这不应该是一个问题,但在某些情况下,BeginSend阻塞导致死锁情况,其中 EndSend 在 WaitUntilNewMessageInQueue 上阻塞(预期),但发送在 BeginSend 上阻塞,因为它似乎等待 EndSend 回调返回(不是预期的)。
这种行为不是我所期待的。如果回调没有及时返回,为什么BeginSend有时会阻塞?
答案 0 :(得分:2)
首先,为什么要锁定Send
方法?由于您使用的是BeginSend
,因此锁定将在发送完成之前释放。结果是多个发送可以同时执行。
其次,不要写(r) => EndSend(r)
,只需写EndSend
(不带任何参数)。
Thrid:您不需要在您的州内包含套接字。您的EndSend
方法与其他任何实例方法一样。因此,您可以直接访问socket
字段。
至于你的僵局,很难说。您委托可能与它有关(由编译器/运行程序进行优化)。但我对那个领域一无所知。
需要更多帮助?发布更多代码。但我建议你解决上面的问题(所有四个问题),然后再试一次。
答案 1 :(得分:1)
您在运行哪种操作系统?
你确定你看到了你认为你所看到的东西吗?
MSDN page上的注释表示如果没有OS缓冲区空间来启动异步发送,Send()
CAN会阻塞,除非您已将套接字置于非阻塞模式。可能是这样吗?您是否可能非常快速地发送数据并将TCP窗口填充到对等方?如果你闯入调试器,调用堆栈会显示什么?
其余的是基于我对所涉及的基础本地技术的理解而进行的猜测......
如果线程退出,Send()
的注释可能是错误的I / O被取消,这几乎肯定取决于底层操作系统,因为它是一个低级IO完成端口/重叠的I / O问题,随之改变Windows Vista(请参阅此处:http://www.lenholgate.com/blog/2008/02/major-vista-overlapped-io-change.html)并且鉴于他们错了,那么他们可能会错误地了解如何在以后的操作系统上调度完成(对EndSend()
的调用)。从Vista开始,如果.Net套接字包装器在套接字上启用了正确的选项,则可能会在发布线程上调度完成(请参阅here我在哪里谈论FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
)...但是如果是这种情况,那么很可能你会看到这种行为很多,因为最初大多数发送都可能完成“排队”,所以你会看到大多数完成发生在同一个线程上 - 我很确定情况并非如此,.Net不会在不询问...
答案 2 :(得分:0)
这是检查它是否同步完成的方式,以避免在另一个线程上回调。
单次发送:
var result = socket.BeginSend(...);
if (result.CompletedSynchronously)
{
socket.EndSend(result);
}
对于多个发送的队列,您可以循环并完成所有同步发送:
while (true)
{
var result = socket.BeginSend(...);
if (!result.CompletedSynchronously)
{
break;
}
socket.EndSend(result);
}