【问题标题】:Callback method indirectly calling back itself回调方法间接回调自身
【发布时间】:2014-01-08 06:44:03
【问题描述】:

在 C# 类中,以下格式用于从套接字异步读取数据。

正如您在代码中看到的,它使用AsyncReceive 从套接字读取数据。接收到数据时回调OnDataReception

OnDataReception 中,处理接收到的数据,如果对话尚未结束,则再次调用ListenForData

这段代码 sn-p 会创建任何类型的无限递归吗? (函数或线程)

class ClientConnection{
  Socket socket = ...
  SocketAsyncEventArgs args = ...
  args.Completed += new EventHandler<SocketAsyncEventArgs>(OnDataReception);
  ...

  public void ListenForData(){
    ...
    socket.ReceiveAsync(args);
    ...
  }

  public void OnDataReception(Object obj, SocketAsyncEventArgs args){
    ...

    // if conversation is finished, return

    // else call ListenForData() again...
  }

  ...
}

【问题讨论】:

    标签: c# recursion asynccallback


    【解决方案1】:

    首先我想提一下,这个问题类似于How does this not cause a stack overflow?

    现在具体说明您的问题:代码 sn-p 使用的线程数量受用于执行 ReceiveAsync 的线程池中可用线程数量的限制。如果返回了对 async 方法的先前调用,则只会生成新线程。

    另外OnDataReception 每次都会打电话给ListenForDataListenForData 不直接调用OnDataReception。这意味着,这两种方法之间没有直接的函数递归。 ReceiveAsync 在后台线程中执行不会产生嵌套堆栈帧。

    所以在我看来,代码中没有隐藏的递归。

    【讨论】:

      【解决方案2】:

      MSDN Documentation on Socket.ReceiveAsync:

      如果 I/O 操作处于挂起状态,则返回 true。这 将引发 e 参数上的SocketAsyncEventArgs.Completed 事件 手术完成后。

      如果 I/O 操作返回 false 同步完成。在这种情况下, e 参数上的SocketAsyncEventArgs.Completed 事件将不会 引发并且可以检查作为参数传递的 e 对象 方法调用返回后立即检索结果 操作。

      要回答您的问题,它不会创建无限循环/递归。

      您的ListenForData 方法每次调用ReceiveAsync 时只会调用一次OnDataReception。由于您在 OnDataReception 中调用 ListenForData 而不是在循环中,因此它将如下所示:

      ListenForData called!
      (time elapses)
      OnDataReception called!
      ListenForData called!
      (time elapses)
      OnDataReception called!
      ListenForData called!
      ...
      

      这几乎与使用Socket.BeginReceive 相同,传递一个AsyncCallback,然后在回调中调用Socket.EndReceive,后跟另一个Socket.BeginReceive。这是一个无限循环,但不是一个无限循环。

      见:

      void StartReceiving()
      {
        // Start receiving asynchronously...
        socket.BeginReceive(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, OnDataReceived, null);
      }
      
      void OnDataReceived(IAsyncResult result)
      {
        // Finish receiving this data.
        var numberOfBytesReceived = socket.EndReceive(result);
      
        // Start receiving asynchronously again...
        if(numberOfBytesReceived > 0 && socket.Connected)
          socket.BeginReceive(recvBuffer, 0, recvBuffer.Length, SocketFlags.None, OnDataReceived, null);
      }
      

      【讨论】:

        【解决方案3】:

        这不会产生任何无限递归,但您可以稍微更改架构以有效利用资源。 您可以使用两个不同的线程进行侦听和转换,因为程序需要侦听端口而与转换结果无关。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-04-03
          • 1970-01-01
          • 1970-01-01
          • 2017-12-10
          • 2014-06-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多