好书说,在实现异步编程模型时,总是有可能连续多次同步调用回调,导致堆栈溢出。问题是我甚至无法接近复制这种情况。甚至下面的程序,它通过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);
}
}
}
答案 0 :(得分:0)
如果在一个单独的线程的堆栈中放入太多对象,则可能会发生堆栈溢出。异步new Action(..).BeginInvoke()
或实际上任何委托BeginInvoke
都使用可用线程from thread pool,因此任何新的异步调用都可以:
因此,如果要重现SO异常,请确保只加载一个线程。 BTW,byte[] message
存储在托管堆中。