2010-10-27 225 views
0

虽然试图通过BeginSend调用为队列发送消息,但看起来像是阻塞调用。C#Socket.BeginSend有时似乎同步行为?

Specificly我:

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 callback te return(not expected)。

这种行为并不是我期待的。为什么BeginSend有时会阻止回调没有及时返回?

+0

你试过我的建议? – jgauffin 2010-10-28 12:30:51

+0

是的,虽然我还没有完成。允许异常消除需要重构整个设计。删除锁定会导致部分单元测试失败,但我怀疑这些是可恢复的未处理的异常,但需要进一步调查。 – DefLog 2010-10-29 10:14:45

+0

那么我的问题解决了,但我仍然不清楚为什么我首先发生。我认为自己非常熟悉线程,但显然我对套接字的理解是缺乏的,所以我决定花一些时间来研究这个主题。任何refernces将不胜感激。 – DefLog 2010-11-01 10:20:19

回答

2

首先,你为什么锁定Send方法?由于您使用的是BeginSend,锁定将在发送完成之前释放。结果是多个发送可以同时执行。其次,不要写(r) => EndSend(r),只要写EndSend(不含任何参数)。

Thrid:你不需要在你的状态中包含套接字。您的EndSend方法与其他任何实例方法一样工作。因此您可以直接访问socket字段。

至于你的僵局,很难说。你的委托可能与它有关(编译器/运行器的优化)。但我对这方面不了解。

需要更多帮助?发布更多代码。但我建议你解决上述问题(全部四个),然后再试一次。

+0

我不知道为什么这已被投票,所提出的观点都是有效的。 – 2010-10-27 10:50:48

+0

如果你失望,请善待和激励。 – jgauffin 2010-10-27 11:14:28

+0

对发送的锁定不是为了防止同时发送,而是防止在套接字连接或断开连接时开始发送。将套接字添加到状态的目的是为了防止由于断开连接或配置更改而导致套接字被新实例替换的错误。 – DefLog 2010-10-27 11:45:39

1

你在哪个操作系统上运行?

您确定您看到您认为您所看到的内容吗?

关于MSDN page的注释说Send()如果没有OS缓冲区空间来启动异步发送,除非您已将套接字置于非阻塞模式,否则可能会阻塞。情况会是这样吗?您是否可能非常快速地发送数据并将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),那么可能会在发布线程上调度完成...但是,如果是这种情况,那么它就是很可能你会看到这种行为很多,因为大多数发送可能会“顺利”完成,所以你会看到大多数完成发生在同一个线程上 - 我敢肯定,情况并非如此。净没有启用这个选项没有问...

+0

当我明确将阻塞设置为false后,问题就显现出来了。我仍然对这是为什么有点模糊。 'http://msdn.microsoft.com/en-us/library/bew39x2a.aspx'没有提到阻塞模式,我的测试只使用一次发送和接收,所以垃圾邮件并不是真正的问题。 – DefLog 2010-10-27 13:58:55

+0

我改变了阻塞属性,它似乎工作,但msdn注意到“阻塞属性对异步方法没有影响。”在http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.blocking.aspx,所以我仍然困惑。 – DefLog 2010-10-27 14:27:56

+0

单次发送有多大。当你改变阻塞模式时,阻塞的发送是否实际发送数据?我会假设,如果它返回而不是阻塞,那么它就等于返回E_WOULDBLOCK,这意味着你需要稍后重试操作......尽管如此,所有这些都很奇怪。 – 2010-10-27 16:30:37