Почему асинхронный метод делегата требует вызова EndInvoke? - PullRequest
10 голосов
/ 11 ноября 2009

Почему делегат должен вызывать 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");
        }
    }

Ответы [ 2 ]

16 голосов
/ 11 ноября 2009

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

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

EDIT

Например:

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(); });
6 голосов
/ 11 ноября 2009

Как сказал SLaks, 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();
}
...