IAsyncResult interface and AsyncCallback delegate usage

For a long time, I don't know how to use IAsyncResult  and only know it's a thing has some relations with delegate.

After investgation, I understand it, so write some note at here.

1. IAsyncResult interface is under System namespace.
We can see its defination as follows:

namespace System
{
    public interface IAsyncResult
    {
        // Summary: Gets a user-defined object that qualifies or contains information about an asynchronous operation.
        // Returns: A user-defined object that qualifies or contains information about an asynchronous operation.
        object AsyncState { get; }
        WaitHandle AsyncWaitHandle { get; }
        bool CompletedSynchronously { get; }
        bool IsCompleted { get; }
    }
}

I think the most important thing is the AsyncState object, and we can use it to get the return data from async method.

2. An example (return int value)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplicationAsync
{
    public delegate int LongTimeRuningMehtod(int a, int b);

    public class Program
    {
        static void Main(string[] args)
        {
            Program program = new Program();

            LongTimeRuningMehtod deleteInstance = program.SomeOperate;

            IAsyncResult resultObj = deleteInstance.BeginInvoke(100, 200, null, null);                        
            int result = deleteInstance.EndInvoke(resultObj);

            Console.Write(result);

            Console.ReadKey();

        }

        public int SomeOperate(int x, int y)
        {
            Thread.Sleep(10);
            return x + y;
        }

    }
}
It's a very simple code, and let's suppose runing "SomeOperate" method need a long time. Although it's only a add method, I can add a Thread.Sleep to slow it, HAHA.
We could not get return value from SomeOperate method right now because it's runing or waiting, so we need to create a delegate and perform the method through delegate in another thread, and we can get the return data when it's done.

Please see the two lines in bold.  In order to get return int value from SomeOperate method we need to create a resultObj with IAsyncResult type, and then call .EndInvoke method to get the correct data type (int) from resultObj.

3.Another example (return a class instance)


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace ConsoleApplicationAsync
{
    public delegate Student LongTimeRuningMehtod(string name);

    public class Program
    {
        static void Main(string[] args)
        {
            Program program = new Program();

            LongTimeRuningMehtod deleteInstance = program.SomeOperate;

            IAsyncResult resultObj = deleteInstance.BeginInvoke("Bob", null, null);
                       
            Student result = deleteInstance.EndInvoke(resultObj);

            Console.Write("Name=" + result.Name + " Score=" + result.Score);

            Console.ReadKey();

        }

        public Student SomeOperate(string studentName)
        {           
            Thread.Sleep(10);
            Student s = new Student();
            s.Name = studentName;
            s.Score = 99; 
            return s;
        }

    }

    public class Student
    {
        public string Name { set; get; }
        public int Score { set; get; }
    }
}

So, we can see, it's convenient to get return value from Async method.

4.A Conclusion

The real async period: Before BeginInvoke and after EndInvoke

.. BeginInvoke ... you can do some other thing... EndInvoke ....

For now, there is new problem, you can see the main method is not real async method, the code after EndInvoke can't run till the SomeOperate complete, how to resolve the problem? Can 

SomeOperate notify others when it's done automaticlly?

The answer is CallBack delegate

5. AsyncCallback delegate

We can see its defination as follows:

namespace System
{
    // Summary: References a method to be called when a corresponding asynchronous operation completes.
    // Parameters: ar:The result of the asynchronous operation.
    public delegate void AsyncCallback(IAsyncResult ar);
}

The delegate need a kind of method without return value and has a IAsyncResult parameter.

6.An CallBack example (static delegate instance)

namespace ConsoleApplicationCallBack
{
    public delegate Student LongTimeRuningMehtod(string name);
   
    public class Program
    {
        public static LongTimeRuningMehtod deleteInstance = SomeOperate;
        static void Main(string[] args)
        {
            Program program = new Program();           

            Console.WriteLine("begin 异步执行");

            IAsyncResult resultObj = deleteInstance.BeginInvoke("Bob", program.CallBackMethod, null);

            Console.WriteLine("在异步执行的同时, 可以自由的做些其他的事情,不必等待了");

            Console.ReadKey();

        }

        public void CallBackMethod(IAsyncResult ar)
        {
            Student result = deleteInstance.EndInvoke(ar);
            Console.WriteLine("异步执行结束了, Name=" + result.Name + " Score=" + result.Score);
        }

        public static Student SomeOperate(string studentName)
        {
            Thread.Sleep(1000);
            Student s = new Student();
            s.Name = studentName;
            s.Score = 99;
            return s;
        }

    }

    public class Student
    {
        public string Name { set; get; }
        public int Score { set; get; }
    }
}

In this case, we call .EndInvoke in CallBackMethod method, so we can do other things freely during the async method running.

In fact, it use an async thread (perform SomeOperate method) connect another async thread(perform CallBackMethod method).
But there is a bad smell about the static delegate Instance, can we wirte some non-static delegate instance? Please see 7.

7.CallBack example (non-static delegate instance)

namespace ConsoleApplicationCallBack
{
    public delegate Student LongTimeRuningMehtod(string name);
   
    public class Program
    {        
        static void Main(string[] args)
        {
            Program program = new Program();           

            Console.WriteLine("begin 异步执行");

            AsyncCallback callBackInstance = program.CallBackMethod;

            LongTimeRuningMehtod deleteInstance = SomeOperate;

            IAsyncResult resultObj = deleteInstance.BeginInvoke("Bob", callBackInstance, deleteInstance);

            Console.WriteLine("在异步执行的同时, 可以自由的做些其他的事情,不必等待了");

            Console.ReadKey();
        }

        public void CallBackMethod(IAsyncResult ar)
        {
              LongTimeRuningMehtod deleteInstance = (LongTimeRuningMehtod)ar.AsyncState;
              Student result = deleteInstance.EndInvoke(ar);
              Console.WriteLine("异步执行结束了, Name=" + result.Name + " Score=" + result.Score);
        }

        public static Student SomeOperate(string studentName)
        {
            Thread.Sleep(1000);
            Student s = new Student();
            s.Name = studentName;
            s.Score = 99;
            //throw Exception("ttt");
            return s;
        }

    }

    public class Student
    {
        public string Name { set; get; }
        public int Score { set; get; }
    }
}

Please see the two lines in bold. The key is send current delegate instance as a parameter, and then get it from ar.AsyncState.

But let's consider if there is an exception in SomeOperate method, how to catch it?

8.Deal with Exception  

namespace ConsoleApplicationCallBack
{
    public delegate Student LongTimeRuningMehtod(string name);
   
    public class Program
    {
       
        static void Main(string[] args)
        {
            Program program = new Program();           

            Console.WriteLine("begin 异步执行");

            AsyncCallback callBackInstance = program.CallBackMethod;

            LongTimeRuningMehtod deleteInstance = SomeOperate;

            IAsyncResult resultObj = deleteInstance.BeginInvoke("Bob", callBackInstance, deleteInstance);

            Console.WriteLine("在异步执行的同时, 可以自由的做些其他的事情,不必等待了");

            Console.ReadKey();
        }

        public void CallBackMethod(IAsyncResult ar)
        {
            try
            {
                LongTimeRuningMehtod deleteInstance = (LongTimeRuningMehtod)ar.AsyncState;
                Student result = deleteInstance.EndInvoke(ar);
                Console.WriteLine("异步执行结束了, Name=" + result.Name + " Score=" + result.Score);
            }
            catch (Exception ex)
            {
                Console.Write(ex);
            }
        }

        public static Student SomeOperate(string studentName)
        {
            Thread.Sleep(1000);
            Student s = new Student();
            s.Name = studentName;
            s.Score = 99;
            //throw Exception("ttt");
            return s;
        }

    }

    public class Student
    {
        public string Name { set; get; }
        public int Score { set; get; }
    }
}

The correct place is EndInvoke, put try catch on it, you can get the exception message.

For now, I got an perfect solution. (BeginInvoke - EndInvoke - IAsyncResult - AsyncCallback - AsyncState - Exception).

作者:BobLiu
邮箱:lzd_ren@hotmail.com
出处:http://www.cnblogs.com/liuzhendong
本文版权归作者所有,欢迎转载,未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/liuzhendong/p/2177532.html