这怎么不会导致堆栈溢出?

我正在查看使用SocketAsyncEventArgs的服务器的源代码,我正在试图弄清楚这不会导致堆栈溢出:

因此调用此代码以允许套接字接受传入连接(向下滚动到底部以查看我的意思):

///  /// Begins an operation to accept a connection request from the client. ///  /// The context object to use when issuing /// the accept operation on the server's listening socket. private void StartAccept(SocketAsyncEventArgs acceptEventArg) { if (acceptEventArg == null) { acceptEventArg = new SocketAsyncEventArgs(); acceptEventArg.Completed += new EventHandler(OnAcceptCompleted); } else { // Socket must be cleared since the context object is being reused. acceptEventArg.AcceptSocket = null; } this.semaphoreAcceptedClients.WaitOne(); Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg); if (!willRaiseEvent) { this.ProcessAccept(acceptEventArg); } } 

然后在实际接受连接后调用此代码(请参阅最后一行):

  ///  /// Process the accept for the socket listener. ///  /// SocketAsyncEventArg associated with the completed accept operation. private void ProcessAccept(SocketAsyncEventArgs e) { if (e.BytesTransferred > 0) { Interlocked.Increment(ref this.numConnectedSockets); Console.WriteLine("Client connection accepted. There are {0} clients connected to the server", this.numConnectedSockets); } // Get the socket for the accepted client connection and put it into the // ReadEventArg object user token. SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop(); readEventArgs.UserToken = e.AcceptSocket; // As soon as the client is connected, post a receive to the connection. Boolean willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs); if (!willRaiseEvent) { this.ProcessReceive(readEventArgs); } // Accept the next connection request. this.StartAccept(e); // <==== tail end recursive? } 

看最后一行。 它再次调用顶级函数。 这怎么不通过这两个函数之间的来回乒乓来溢出堆栈? 它似乎是尾端递归,但这不是Haskell所以我不知道这是如何工作的。

我的理解是,这些并没有在线程中被触发,而是由cpu一次执行一次。

如果无法立即满足AsyncAccept(或任何AsyncXXX操作),则它将返回true,表示操作将异步完成。 发生这种情况时,callback-event最终会在线程池线程上触发。 即使它编组回到UI线程(因为它是在那里启动的),它也会通过post完成。

AsyncAccept 可能返回true,因为除非有真正挂起的套接字连接(请参阅Listen中的 backlog),否则您正在等待客户端连接。

因此,StartAccept()将在不调用ProcessAccept的情况下退出,并且当它(和如果)触发时,ProcessAccept可能会在不同的线程上。

看看代码:

 if (!willRaiseEvent) { this.ProcessAccept(acceptEventArg); } 

虽然我还没有理解整个机制,但是willRaiseEvent == true会明确地结束递归,所以我猜这种情况会发生,所以它不是无休止的递归。

这一切都取决于willCauseEvent标志,当设置为true时会中断递归。 如果没有挂起的连接,该标志可能设置为true。

很难从上下文中确定,但看起来像第一个直接调用第二个时

 Boolean willRaiseEvent = this.listenSocket.AcceptAsync(acceptEventArg); if (!willRaiseEvent) { 

因此我猜大多数时候它会在完成时引发事件(来自不同的回调线程)?

(另见’堆栈潜水’部分

http://blogs.msdn.com/mjm/archive/2005/05/04/414793.aspx

这似乎是一种类似的事情。)

ProcessAccept() 始终调用StartAccept()但反之则不然。

StartAccept() ,仅当willRaiseEvent设置为true时才调用ProcessAccept() 。 那是你从那里无限递归的退出。

如果你怀疑无限递归,你总是寻找可能的出口点(在一个递归函数或你的一个乒乓函数中,正如你雄辩地说的那样:-)。