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

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

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

/// <summary>
/// Begins an operation to accept a connection request from the client.
/// </summary>
/// <param name="acceptEventArg">The context object to use when issuing 
/// the accept operation on the server's listening socket.</param>
private void StartAccept(SocketAsyncEventArgs acceptEventArg)
{
    if (acceptEventArg == null)
    {
        acceptEventArg = new SocketAsyncEventArgs();
        acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(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);
    }
}

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

  /// <summary>
        /// Process the accept for the socket listener.
        /// </summary>
        /// <param name="e">SocketAsyncEventArg associated with the completed accept operation.</param>
        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线程(因为它是在那里启动),它也会通过帖子完成.

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

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

点赞