防止APM中的堆栈溢出

时间:2012-01-05 17:15:44

标签: c# asynchronous threadpool stack-overflow

好书说,在实现异步编程模型时,总是有可能连续多次同步调用回调,导致堆栈溢出。问题是我甚至无法接近复制这种情况。甚至下面的程序,它通过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);
        }
    }
}

1 个答案:

答案 0 :(得分:0)

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

  • 如果可用,从池中取一个新线程(使用它并返回池中)
  • 等待返回或另外创建的帖子

因此,如果要重现SO异常,请确保只加载一个线程。 BTW,byte[] message存储在托管堆中。