2012-01-05 47 views
1

好書說,在實現異步編程模型時,總會有可能在同一行中多次同步調用回調,導致堆棧溢出。問題是,我甚至無法接近再現這種情況。即使是下面的程序,它通過tcp發送一個大的消息,然後一次讀取一個字節,一次只有幾十次運行,只有幾個級別的遞歸。防止堆棧溢出在APM中

那麼,這真的是一個問題嗎?或者它只是嚇人的,這使得你爲已經龐大的模式增加了兩種額外的方法?如果真的是這樣,爲什麼不直接將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

如果將太多對象放入一個單獨線程的堆棧中,可能會發生堆棧溢出。異步new Action(..).BeginInvoke()或事實上任何代表BeginInvoke使用可用from thread pool線程,因此任何新的異步調用要麼:

  • 採取從池中一個新的線程(如果可用)(使用它並返回到池)
  • 要返回
  • 等待線程或額外創建

所以,如果你想重現SO異常,請確保您加載只有一個線程。 BTW,byte[] message存儲在託管堆中。

+0

異步調用可以同步完成它的工作,並且根本不使用線程池,但前提是它可以非常快地完成任務。從套接字讀取一個字節是我能想到的最小的現實生活任務。 – user1096188 2012-01-05 17:34:03