Почему для асинхронного метода делегата требуется вызов EndInvoke?

Почему делегат должен вызвать EndInvoke до запуска метода? Если мне нужно вызвать EndInvoke (который блокирует поток), значит, это не асинхронный вызов?

Вот код, который пытается запустить.

class Program
    {
        private delegate void GenerateXmlDelegate();

        static void Main(string[] args)
        {
            GenerateXmlDelegate worker = new GenerateXmlDelegate(GenerateMainXml);
            IAsyncResult result = worker.BeginInvoke(null, null);
        }

        private static void GenerateMainXml()
        {
            Thread.Sleep(10000);
            Console.WriteLine("GenerateMainXml Called by delegate");
        }
    }

Ответ 1

Причина, по которой вам нужно позвонить EndInvoke, - это избежать утечек памяти;.Net будет хранить информацию о результате функции (или исключении), пока вы не вызовете EndInvoke.

Вы можете вызвать EndInvoke в обработчике завершения, который вы передаете BeginInvoke, и сохранить асинхронный характер.

ИЗМЕНИТЬ

Например:

class Program {
    private delegate void GenerateXmlDelegate();

    static void Main(string[] args) {
        GenerateXmlDelegate worker = new GenerateXmlDelegate(GenerateMainXml);
        IAsyncResult result = worker.BeginInvoke(delegate {
            try {
                worker.EndInvoke();
            } catch(...) { ... }
        }, null);
    }

    private static void GenerateMainXml() {
        Thread.Sleep(10000);
        Console.WriteLine("GenerateMainXml Called by delegate");
    }
}

Если вы хотите запустить асинхронный вызов и забыть об этом, вы можете использовать ThreadPool, например:

ThreadPool.QueueUserWorkItem(delegate { GenerateMainXml(); });

Ответ 2

Как сказал Слакс, EndInvoke обеспечивает защиту от утечек памяти.

BeginInvoke по-прежнему является асинхронным; рассмотрите следующий код:

static void Main() {
    Func<double> slowCalculator = new Func<double>(PerformSlowCalculation);
    IAsyncResult slowCalculation = slowCalculator.BeginInvoke(null, null);

    // lots of stuff to do while slowCalculator is doing its thing

    Console.WriteLine("Result is {0}", slowCalculator.EndInvoke(slowCalculation));
}

static double PerformSlowCalculation() {
    double result;

    // lots and lots of code

    return result;
}

Если этот код был написан без вызовов BeginInvoke/EndInvoke, PerformSlowCalculation должен был бы закончить до того, как Main мог бы выполнить остальную часть своих "много вещей"; таким образом, оба могут происходить одновременно.

Теперь, в вашем примере с помощью GenerateXmlDelegate, вам все равно нужно EndInvoke, даже если вы ничего не возвращаете. Способ сделать это:

static void Main(string[] args) {
    GenerateXmlDelegate worker = new GenerateXmlDelegate(GenerateMainXml);
    IAsyncResult result = worker.BeginInvoke(GenerateXmlComplete, null);
}

private static void GenerateXmlComplete(IAsyncResult result) {
    AsyncResult realResult = result as AsyncResult;
    GenerateXmlDelegate worker = result.AsyncDelegate as GenerateXmlDelegate;
    worker.EndInvoke();
}