Защита от переполнения стека в APM - PullRequest
1 голос
/ 05 января 2012

В доброй книге говорится, что при реализации модели асинхронного программирования всегда существует возможность синхронного вызова обратного вызова много раз подряд, что приводит к переполнению стека.Проблема в том, что я даже не могу приблизиться к воспроизведению этой ситуации.Даже приведенная ниже программа, которая отправляет себе большое сообщение через 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 голосов
/ 05 января 2012

Переполнение стека может произойти, если вы поместите слишком много объектов в стек одного отдельного потока.Async new Action(..).BeginInvoke() или фактически любой делегат BeginInvoke использует потоки, доступные из пула потоков , поэтому любой новый асинхронный вызов будет либо:

  • Извлечь новый поток из пула, еслидоступно (используйте его и верните обратно в пул)
    или
  • Подождите, пока поток не будет возвращен или дополнительно создан

Так что если вы хотите воспроизвести исключение SO,убедитесь, что вы загружаете только один поток.Кстати, byte[] message хранится в управляемой куче.

...