- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我已经继承了一个正在尝试调试的套接字回调例程。它使用 EndReceive() 进行异步 TCP 通信,紧接着是 BeginReceve(),因此它始终处于监听状态。它充满了显示消息并继续进行诊断的“命中时”断点。
因为它是一个异步回调,所以它可能会被系统多次调用一个BeginReceive(),即使它正在处理上一个回调。通常这是在一个新线程中,这就是为什么我对整个事情都有一个锁(一个关键部分)。但有时它似乎在退出之前就被同一个线程重新进入。这怎么可能?我做错了什么?
他是我的踪迹(使用'When Hit'断点)。 . .
Socket_DataArrival() – Entering … managedThreadID=11
Socket_DataArrival() - first statement in lock . . . managedThreadID=11
Socket_DataArrival() - EndReceive() ... managedThreadID=11
Socket_DataArrival() - BeginReceive() . . . managedThreadID=11
Socket_DataArrival() – Entering … managedThreadID=11
Socket_DataArrival() - first statement in lock . . . managedThreadID=11
Socket_DataArrival() - EndReceive() ... managedThreadID=11
Socket_DataArrival() - BeginReceive() . . . managedThreadID=11
Socket_DataArrival() - exiting at end of routine . . . managedThreadID=11
Socket_DataArrival() - exiting at end of routine . . . managedThreadID=11
这是例程(专有代码已注释掉)。 . .
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;
}
inputLock 在类级别定义为 . . .
private Object inputLock = new Object();
同一个线程如何在第一次退出之前第二次进入它?
最佳答案
看起来,当您调用 BeginReceive()
并且数据已经可用时,您会立即在调用 BeginReceive()
的线程上收到回调。
这听起来完全合理,因为底层 I/O 模型几乎可以保证是基于 I/O 完成端口的,并且在 Windows XP 之后的操作系统(因此所有当前支持的操作系统)上,您可以告诉 IOCP“跳过完成端口处理”成功时',而是立即将异步数据返回给调用者。
因此,我假设正在发生的事情是 BeginReceive()
向下调用 WSARecv()
并立即完成,因为数据可用,因此调用代码会执行立即在调用线程上回调。如果没有可用数据,则 WSARecv()
将返回 IO_PENDING
并且 I/O 最终将完成(当数据到达时)和关联的线程之一然后使用套接字的 IOCP 处理完成并调用处理程序。
某些数据流模式比其他模式更容易导致这种情况发生,当然,这取决于网络和数据的流动方式。异步发送也更有可能发生...
修复它的最佳方法是让您的 I/O 处理程序简单地将所有完成放入一个队列中,该队列只有一个线程可以处理并且不能递归处理。然后,这为您提供了一个不会发生此问题的设计。我写过这样的设计here ,用于 ACCU 出版物 Overload。
关于c# - 为什么我会得到这个结果(同一个线程在退出前进入两次)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27020692/
我是一名优秀的程序员,十分优秀!