-->

Why does asynchronous delegate method require call

2019-01-07 21:38发布

问题:

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

回答1:

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


回答2:

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