gpt4 book ai didi

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

转载 作者:太空狗 更新时间:2023-10-30 00:40:33 24 4
gpt4 key购买 nike

我已经继承了一个正在尝试调试的套接字回调例程。它使用 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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com