这是一个异步读取吗?

3

我有一个listen()函数,它正在读取网络流和一个回调函数newDataRecievedCallback。

我调用了一个名为BeginRead的方法,它是异步的,但我在回调函数中再次调用了相同的方法。那么这不是同步逻辑吗?

还有其他方法可以实现吗?

    private void listen()
    {
        networkStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(newDataRecievedCallback), null);
    }

    private void newDataRecievedCallback(IAsyncResult rst)
    {
        try
        {
            int recievedDataSize = tcpClient.Client.Receive(buffer);
            recievedData = convertToString(buffer, incomeDataSize);

            //End Read
            networkStream.EndRead(rst);
            cleanBuffer();
            parseXMLData(recievedData);

            //Hier I call the same async method
            networkStream.BeginRead(buffer, 0, buffer.Length, new AsyncCallback(newDataRecievedCallback), null);
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
2个回答

4

如果 BeginRead 总是异步完成,那么在回调函数中再次调用它仍然是异步的。

然而,BeginRead 有时会同步完成(检查 IAsyncResult.CompletedSynchronously),因此当你运气不佳时,你的代码就容易遭受堆栈溢出的风险。例如,在一个线程中可能会发生这种情况:newDataRecievedCallback -> BeginRead -> newDataRecievedCallback -> BeginRead 等等。

使用 BeginRead 的正确方式是使用类似下面的模式(这是来自于 C# 4.0 in a Nutshell 的一个 代码片段)。实质上,你应该始终检查方法是否同步完成,然后采取相应的行动。

void Read()            // Read in a nonblocking fashion.
{
  while (true)
  {
    IAsyncResult r = _stream.BeginRead
     (_data, _bytesRead, _data.Length - _bytesRead, ReadCallback, null);

    // This will nearly always return in the next line:
    if (!r.CompletedSynchronously) return;   // Handled by callback
    if (!EndRead (r)) break;
  }
  Write();
}

void ReadCallback (IAsyncResult r)
{
  try
  {
    if (r.CompletedSynchronously) return;
    if (EndRead (r))
    {
      Read();       // More data to read!
      return;
    }
    Write();
  }
  catch (Exception ex) { ProcessException (ex); }
}

bool EndRead (IAsyncResult r)   // Returns false if there’s no more data
{
  int chunkSize = _stream.EndRead (r);
  _bytesRead += chunkSize;
  return chunkSize > 0 && _bytesRead < _data.Length;   // More to read
}

3

仍然是异步的,因为您调用 networkStream.BeginRead 并不会阻塞。您发出调用后就退出函数。是的,它将再次被调用,但仍然是异步方式。

还有其他方法吗?是的,数百种方法。您的代码并不差。只是似乎有点耦合紧密,因为您的异步处理程序正在执行自己的管理以及处理数据。更清晰的方法是创建某种控制器,您的 newDataRecievedCallback 通过委托通知该控制器,并将数据传递给其进行处理。控制器还负责启动下一个异步过程。另外,单独的控制器可以将接收到的数据传递给处理,而不会阻塞更多的异步调用。


即使是"BeginRead"有时也会完成同步操作。他的代码容易出现堆栈溢出问题。 - Ilian
@Paul Sasik,请给出一个示例会很好。 - Racooon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接