C# APM中栈溢出的防范

C# APM中栈溢出的防范,c#,asynchronous,threadpool,stack-overflow,C#,Asynchronous,Threadpool,Stack Overflow,这本好书说,在实现异步编程模型时,总是有可能在一行中多次同步调用回调,从而导致堆栈溢出。问题是我甚至无法再现这种情况。即使是下面的程序,它通过tcp向自己发送一条大消息,然后一次读取一个字节,几十次运行中只有一次会陷入递归,只有几个层次 那么,这真的是个问题吗?还是仅仅是散布恐慌,让你在一个已经庞大的模式中增加了两个额外的方法?如果确实如此,为什么不将BeginMethod放在线程池的队列中,而不是在同一线程上执行回调时直接调用它呢 using System; using System.Linq

这本好书说,在实现异步编程模型时,总是有可能在一行中多次同步调用回调,从而导致堆栈溢出。问题是我甚至无法再现这种情况。即使是下面的程序,它通过tcp向自己发送一条大消息,然后一次读取一个字节,几十次运行中只有一次会陷入递归,只有几个层次

那么,这真的是个问题吗?还是仅仅是散布恐慌,让你在一个已经庞大的模式中增加了两个额外的方法?如果确实如此,为什么不将BeginMethod放在线程池的队列中,而不是在同一线程上执行回调时直接调用它呢

using System;
using System.Linq;
using System.Net.Sockets;
using System.Net;

class Program {
    static byte[] buff = new byte[1];
    static int cntr = 0;
    static void Main(string[] args) {
        var listener = new TcpListener(IPAddress.Loopback, 11111);
        listener.Start();
        new Action(() => {
            byte[] message = Enumerable.Range(0, 100000).Select(i => (byte)i).ToArray();
            var client = new TcpClient();
            client.Connect(IPAddress.Loopback, 11111);
            using (var s = client.GetStream()) s.Write(message, 0, message.Length);
        }).BeginInvoke(null, null);
        var stream = listener.AcceptTcpClient().GetStream();
        stream.BeginRead(buff, 0, buff.Length, callback, stream);
        Console.ReadLine();
    }
    static void callback(IAsyncResult iar) {
        if (iar.CompletedSynchronously) Console.Write(cntr++ +" "); else cntr=0;
        var stream = iar.AsyncState as NetworkStream;
        if (stream.EndRead(iar) > 0) {
            stream.BeginRead(buff, 0, buff.Length, callback, stream);
        }
    }
}

若在单个线程的堆栈中放置太多对象,则可能会发生堆栈溢出。Async
new Action(..).BeginInvoke()
或实际上任何委托
BeginInvoke
都使用可用的线程,因此任何新的异步调用都会:

  • 从池中获取新线程(如果可用)(使用它并返回池)
  • 等待返回或额外创建线程

因此,如果您想复制So异常,请确保只加载一个线程。顺便说一句,
byte[]消息
存储在托管堆中。

异步调用可以同步完成其作业,并且根本不使用线程池,但前提是它可以非常快地完成其任务。从套接字读取一个字节是我能想到的最小的现实任务。