【问题标题】:Guarding against stack overflows in APM防止 APM 中的堆栈溢出
【发布时间】:2012-02-03 12:34:46
【问题描述】:

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

【问题讨论】:

    标签: c# asynchronous threadpool stack-overflow


    【解决方案1】:

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

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

    因此,如果您想重现 SO 异常,请确保仅加载一个线程。顺便说一句,byte[] message 存储在托管堆中。

    【讨论】:

    • 异步调用可以同步完成它的工作并且根本不使用线程池,但前提是它可以非常快速地完成它的任务。从套接字读取一个字节是我能想到的最小的现实生活任务。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-10-02
    • 2011-06-24
    • 1970-01-01
    • 2010-10-29
    • 2013-11-15
    • 2012-08-24
    • 1970-01-01
    相关资源
    最近更新 更多