C# 为什么我会得到这个结果(同一个线程在退出之前进入两次)?

C# 为什么我会得到这个结果(同一个线程在退出之前进入两次)?,c#,multithreading,sockets,C#,Multithreading,Sockets,我继承了一个试图调试的套接字回调例程。它与EndReceive()进行异步TCP通信,紧接着是BeginReceive(),因此它总是在侦听。它充满了“命中时”断点,显示消息并继续进行诊断 因为它是异步回调,系统可以多次调用一个开始接收(),即使它在处理先前回调的中间。通常这是在一个新的线程中,这就是为什么我有一个锁(一个关键部分)用于整个事情。但有时它似乎在退出之前就被同一个线程重新进入了。这怎么可能?我做错了什么 他是我的跟踪(使用“命中时”断点) 下面是例行程序(专有代码注释掉) inpu

我继承了一个试图调试的套接字回调例程。它与EndReceive()进行异步TCP通信,紧接着是BeginReceive(),因此它总是在侦听。它充满了“命中时”断点,显示消息并继续进行诊断

因为它是异步回调,系统可以多次调用一个开始接收(),即使它在处理先前回调的中间。通常这是在一个新的线程中,这就是为什么我有一个锁(一个关键部分)用于整个事情。但有时它似乎在退出之前就被同一个线程重新进入了。这怎么可能?我做错了什么

他是我的跟踪(使用“命中时”断点)

下面是例行程序(专有代码注释掉)

inputLock在类级别定义为

private Object inputLock = new Object();

同一个线程如何在第一次退出之前再次进入它

调用BeginReceive()时,允许下一次回调(在该线程中)。在释放锁后放置BeginReceive。

一个
只会阻止一个不同的线程进入临界段,而不是同一个

由于您使用相同的回调调用了
套接字中的
\u TCPConn.BeginReceive
,因此当从
\u TCPConn
接收数据时,只需再次输入此回调


尝试对
\u TCPConn
使用不同的回调,或者尝试在
Socket\u DataArrival()
之外调用
BeginReceive()

调用
BeginReceive()
时,当您调用
BeginReceive()
并且数据已经可用时,您会立即在调用
BeginReceive()
的线程上得到回调

这听起来完全有道理,因为基本I/O模型几乎可以保证是基于I/O完成端口的,并且基于Windows XP之后的操作系统(因此所有当前支持的OSs)。您可以告诉IOCP“成功时跳过完成端口处理”,而是立即将异步数据返回给调用方

因此,我假设发生的是,
BeginReceive()
向下调用
WSARecv()
,这会立即完成,因为数据可用,因此调用代码会在调用线程上立即执行回调。如果没有可用的数据,
WSARecv()
将返回
IO_PENDING
,I/O最终将完成(当数据到达时),与套接字的IOCP关联的一个线程将处理完成并调用处理程序

某些数据流模式比其他模式更可能导致这种情况发生,当然,这取决于网络和数据的流动方式。异步发送也更可能发生这种情况


修复此问题的最佳方法是让I/O处理程序简单地将所有完成操作放入一个队列中,该队列只有一个线程可以处理,并且不能递归处理。这就给你留下了一个设计,让这个问题不会发生。我为ACCU出版物过载写了这样一个设计

您是否尝试过调试它,或者将整个堆栈跟踪放在日志中?似乎您在这里输入了递归,在同一个线程上-您在任何地方都有相同的ID。是否确定已异步调用套接字\u DataArrival?如果在函数项处打印堆栈跟踪,是否看到前面的Socket_DataArrival调用?在这个S.O.问题中,它说应该在EndReceive()之后立即执行BeginReceive()。你似乎在建议一种不同的设计模式。使用EndReceive()/BeinReceive()进行异步通信的正确.Net设计模式是什么?该链接没有代码示例,但我认为他们没有谈论使用信号量或锁。代码的锁定区域应该尽可能短,并且在其中没有更多异步行为的选项。我同意@NerfHerder的观点
   private void Socket_DataArrival(IAsyncResult ar)
   {
       StateObject stateObject;
       int bytesReceived;
       int managedThreadId = Thread.CurrentThread.ManagedThreadId;

       lock (inputLock)
       {                 // "Entering..."
           try
           {
               _Receiving = false;  //"first statement in lock"
               stateObject = (StateObject)ar.AsyncState;
               bytesReceived = stateObject.sSocket.EndReceive(ar);    
               _Receiving = true;

               _StateObject = new StateObject(2048, _TCPConn);  //2048 = arbitrary number
               _StateObject.tag = "Socket_DataArrival ";

               _TCPConn.BeginReceive(
                    _StateObject.sBuffer,
                    0,
                    _StateObject.sBuffer.Length,
                    SocketFlags.None,
                    new AsyncCallback(Socket_DataArrival),
                    _StateObject);

            }
            catch (Exception exc)
            {
                subs.LogException("Socket_DataArrival", exc);
            }

            // proprietary stuff goes here

        }  // end critical section
        return;
    }
private Object inputLock = new Object();