Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#Socket.BeginSend有时似乎是同步运行的?_C#_Sockets - Fatal编程技术网

C#Socket.BeginSend有时似乎是同步运行的?

C#Socket.BeginSend有时似乎是同步运行的?,c#,sockets,C#,Sockets,当试图通过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,

当试图通过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 en EndSend时,会发生这种情况。WaitUntilNewMessageInQueue会一直阻塞,直到队列中有新消息为止,因此当没有新消息时,它可以等待退出一段时间

据我所知,这应该不是一个真正的问题,但在某些情况下,BeginSend阻塞会导致死锁,EndSend阻塞WaitUntilNewMessageInQueue(预期),而Send阻塞BeginSend作为回报,因为它似乎在等待EndSend回调te返回(不预期)


这种行为不是我所期望的。如果回调没有及时返回,为什么BeginSend有时会阻塞?

首先,为什么要锁定
Send
方法?由于您使用的是
BeginSend
,因此在发送完成之前将释放锁。结果是可以同时执行多个发送

其次,不要写
(r)=>EndSend(r)
,只写
EndSend
(没有任何参数)

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

至于你的僵局,很难说。委托可能与此有关(由编译器/运行程序进行优化)。但我对这方面一无所知


需要更多的帮助吗?发布更多代码。但是我建议您解决上述问题(全部四个),然后首先重试。

首先,为什么要锁定
发送方法?由于您使用的是
BeginSend
,因此在发送完成之前将释放锁。结果是可以同时执行多个发送

其次,不要写
(r)=>EndSend(r)
,只写
EndSend
(没有任何参数)

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

至于你的僵局,很难说。委托可能与此有关(由编译器/运行程序进行优化)。但我对这方面一无所知


需要更多的帮助吗?发布更多代码。但我建议您解决上述问题(全部四个),然后首先重试。

您运行的是哪种操作系统

你确定你看到的是你认为你看到的吗

上的注释指出,如果没有操作系统缓冲区空间来启动异步发送,则
Send()
可能会阻塞,除非您将套接字置于非阻塞模式。是这样吗?您是否可能非常快速地发送数据并将TCP窗口填充到对等方?如果闯入调试器,调用堆栈会显示什么

其余的是基于我对所涉及的底层本地技术的理解的推测


如果线程退出,
Send()
的注释可能错误地认为I/O被取消,这几乎肯定取决于底层操作系统,因为这是一个低级别的IO完成端口/重叠I/O问题,随着Windows Vista的改变(请参见此处:),如果他们在这方面有错误,那么他们可能在完成方式上有错误(对
EndSend()
的调用在以后的操作系统上进行调度)。从Vista开始,如果.Net套接字包装器在套接字上启用了正确的选项,则可能会在发出线程上调度完成操作(请参见我在何处谈到的
文件\u跳过\u完成\u端口\u成功
)…然而,如果是这种情况,那么您可能会经常看到这种行为,因为最初大多数发送都可能“在线”完成,因此您会看到大多数完成都发生在同一个线程上-我非常确定情况并非如此,.Net在不询问您是哪种操作系统的情况下不会启用此选项…

继续跑

你确定你看到的是你认为你看到的吗

上的注释说,如果没有操作系统缓冲区空间来启动异步发送,则
Send()
可能会被阻止,除非您将套接字置于非阻止模式。可能是这种情况吗?您是否可能非常快地发送数据并将TCP窗口填充到对等方?如果您闯入调试器,调用堆栈会显示什么

其余的是基于我对所涉及的底层本地技术的理解的推测


如果线程退出,
Send()
的注释可能错误地认为I/O被取消,这几乎肯定取决于底层操作系统,因为这是一个低级别的IO完成端口/重叠I/O问题,随着Windows Vista的改变(请参见此处:),如果他们在这方面有错误,那么他们可能在完成方式上有错误(对
EndSend()
的调用在以后的操作系统上进行调度)。从Vista开始,如果.Net套接字包装器在套接字上启用了正确的选项,则可能会在发出线程上调度完成操作(请参见我在何处谈到的
文件\u跳过\u完成\u端口\u成功
)…然而,如果是这种情况,那么您很可能会经常看到这种行为,因为最初大多数发送可能是“在线”完成的,因此您会看到大多数完成发生在同一个线程上-我非常确定情况并非如此,而且.Net在没有询问的情况下不会启用此选项…

这就是您检查它是否符合同步完成,因此可以避免在另一个线程上进行回调

对于单个发送:

var result = socket.BeginSend(...);
if (result.CompletedSynchronously)
{
    socket.EndSend(result);
}
对于一队mu
while (true)
{
    var result = socket.BeginSend(...);
    if (!result.CompletedSynchronously)
    {
        break;
    }
    socket.EndSend(result);
}