Why does asynchronous delegate method require calling EndInvoke?
Asked Answered
H

2

10

Why does the delegate need to call the EndInvoke before the method fires? If i need to call the EndInvoke (which blocks the thread) then its not really an asynchronous call is it?

Here is the code im trying to run.

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");
        }
    }
Harve answered 11/11, 2009 at 3:4 Comment(0)
E
17

The reason you need to call EndInvoke is to avoid memory leaks; .Net will store information about the function's result (or exception) until you call EndInvoke.

You can call EndInvoke in the completion handler that you give to BeginInvoke and retain the asyncronous nature.

EDIT:

For example:

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");
    }
}

If you want to fire an async call and forget about it, you can use the ThreadPool, like this:

ThreadPool.QueueUserWorkItem(delegate { GenerateMainXml(); });
Enact answered 11/11, 2009 at 3:6 Comment(2)
sorry could you possible extend my example to demonstrate what you mean by Completion Handler? From all the articles i've read it suggests that by simply calling BeginInvoke will fire the method call.Harve
This is only somewhat correct - no memory leaks will occur. gist.github.com/jcdickinson/9109599 . In certain scenarios, however, some tracking is done with Begin/End-Invoke pairs - one example is: if you don't call End* on Socket operations your socket performance counters will go completely out of whack (no memory leak, the values will just be wildly incorrect).Gamo
S
6

As SLaks said, EndInvoke insures against memory leaks.

BeginInvoke is still asynchronous; consider the following code:

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;
}

If this code were written without the BeginInvoke/EndInvoke calls, PerformSlowCalculation would have to finish before Main could do the rest of its "lots of stuff"; this way, the two can be happening at the same time.

Now, in your example using a GenerateXmlDelegate, you still need EndInvoke even though you're not returning anything. The way to do this is:

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();
}
Smitty answered 11/11, 2009 at 3:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.