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