C#高级编程之Invoke和BeginInvoke

Invoke同步(按顺序)与BeginInvoke异步(无序)

Invoke

在拥有此控件的基础窗口句柄的线程上执行委托,同步的。

BeginInvoke

在创建控件的基础句柄所在线程上异步执行委托。

如下所示:

         /// <summary>
        /// 同步方法:发起调用,代码按照顺序逐行执行。---有顺序(线程ID是同一个--同一线程来执行的所有操作)
        /// </summary>
        private static void ExecuteSyncMethod()
        {
            Action<string> action = new Action<string>(DoSomeThing);
            action.Invoke("同步执行 DoSomeThing 0");
            Console.WriteLine("---------------------");
        }
//异步方法:发起调用:没有等待完成,直接进入到下一行,启动一个新的线程来执行动作(线程ID不一样)
        //异步方法:不卡顿界面:UI(主线程)闲置,计算任务交给了子线程去执行了。
        //同步方法:卡界面,因为主线程忙于计算,根本无暇他顾。
        /*异步调用Async
         开辟一个新的线程去执行BeginInvoke[调用DoSomeThing方法],主线程不会等待这句话执行完,会立即去执行
        下面的action("同步执行 DoSomeThing 1");action("同步执行 DoSomeThing 2");语句
         异步调用Async是无序的,那如果想按照顺序实现异步,那怎么办呢,使用回调函数
         */
        private static void ExecuteAsyncMethod(Action<string> action)
        {
            //异步无序调用 .net core不支持BeginInvoke异步调用,是个坑
            action.BeginInvoke("异步无序执行 DoSomeThing", null, null);
            action("同步执行 DoSomeThing 1");
            action("同步执行 DoSomeThing 2");
            action("同步执行 DoSomeThing 3");
            Console.WriteLine("执行结束");
            Console.ReadKey();
        }
static void Main(string[] args)
        {
ExecuteSyncMethod();
Action<string> action = new Action<string>(DoSomeThing);
ExecuteAsyncMethod(action);

 可以看到同步的线程ID与BeginInvoke启动的线程ID是不同的。

下面的例子与上面相似(BeginInvoke启动的是一个无序的新线程)

    private static void ExecuteCallBackFunc()
        {
            Console.WriteLine($"Func start,threadID={Thread.CurrentThread.ManagedThreadId}");
            Action<string> action = new Action<string>(DoSomeThing);
            action.BeginInvoke("Excuting Method", null, null);
            Console.WriteLine("ExecuteCallBackFunc is execute filished1");
            Console.WriteLine("ExecuteCallBackFunc is execute filished2");
            Console.WriteLine("ExecuteCallBackFunc is execute filished3");
        }

BeginInvoke下的实现顺序执行

无参回调函数

那如果想 Executing Method执行完后再按照顺序打印"execute filished",如果做呢?可以通过回调函数,如下所示:

    private static void DoSomeThing(string methodname)
        {
            Thread.Sleep(1000);
            Console.WriteLine($"{DateTime.Now.ToString()} method: {methodname},threadID=[{Thread.CurrentThread.ManagedThreadId}]");
        }

        private static void ExecuteCallBackFunc()
        {
            Console.WriteLine($"Func start,threadID={Thread.CurrentThread.ManagedThreadId}");
            Action<string> action = new Action<string>(DoSomeThing);
            //public delegate void AsyncCallback(IAsyncResult ar);AsyncCallback是一个形参为IAsyncResult的无返回值的委托
            //异步回调:执行完Excuting Method的动作后才执行execute filished1【相当于一堆动作执行完毕后,再执行点动作--回调】
            AsyncCallback callback = arr =>
            {
                Console.WriteLine("ExecuteCallBackFunc is execute filished1");
                Console.WriteLine("ExecuteCallBackFunc is execute filished2");
                Console.WriteLine("ExecuteCallBackFunc is execute filished3");
            };
            action.BeginInvoke("Excuting Method", callback, null);

        }

 带参数回调函数

 如果需要往异步回调委托中传递参数,如果传递呢?可以通过给BeginInvoke第三个参数传值,在异步委托的内部,可以通过AsyncState获取到参数的值

有返回值的异步委托使用

         //2如果想要获取返回值呢
            Func<int> func = () =>
            {
                Thread.Sleep(3000);
                return DateTime.Now.Year;
            };
            IAsyncResult asyncResult = func.BeginInvoke(callback, "weiyin");
            while (!asyncResult.IsCompleted)
            {
                Console.WriteLine("1222222");
                /*Thread.Sleep(20)*/;
            }

上面asyncResult描述了委托执行的情况,如是否执行完毕,是否需要等待等相关属性。IAsyncResult接口信息如下:

//     表示异步操作的状态。
    [ComVisible(true)]
    public interface IAsyncResult
    {
        //
        // 摘要:
        //     获取一个值,该值指示异步操作是否已完成。
        //
        // 返回结果:
        //     如果操作已完成,则为 true;否则为 false。
        bool IsCompleted { get; }
        //
        // 摘要:
        //     获取用于等待异步操作完成的 System.Threading.WaitHandle。
        //
        // 返回结果:
        //     用于等待异步操作完成的 System.Threading.WaitHandle。
        WaitHandle AsyncWaitHandle { get; }
        //
        // 摘要:
        //     获取一个用户定义的对象,该对象限定或包含有关异步操作的信息。
        //
        // 返回结果:
        //     一个用户定义的对象,限定或包含有关异步操作的信息。
        object AsyncState { get; }
        //
        // 摘要:
        //     获取一个值,该值指示异步操作是否同步完成。
        //
        // 返回结果:
        //     如果异步操作同步完成,则为 true;否则为 false。
        bool CompletedSynchronously { get; }
    }
View Code

EndInvoke获取异步委托的返回值

而如何获取该委托执行后的结果呢?通过执行EndInvoke接收异步委托的返回值。

    Console.WriteLine($"Func start,threadID={Thread.CurrentThread.ManagedThreadId}");
            AsyncCallback callback = arr =>
            {
                Console.WriteLine("传入的参数为:"+arr.AsyncState);
                Console.WriteLine($"threadID=[{Thread.CurrentThread.ManagedThreadId}]");
                Console.WriteLine("ExecuteCallBackFunc is execute filished1");
                Console.WriteLine("ExecuteCallBackFunc is execute filished2");
                Console.WriteLine("ExecuteCallBackFunc is execute filished3");
            };
            //如果需要往异步回调委托中传递参数,就给BeginInvoke第三个参数传值,在异步委托的内部,可以通过AsyncState获取到参数的值
            //1.控制了顺序:通过异步回到委托
            //action.BeginInvoke("Excuting Method", callback, "this is weiyin");
            //2如果想要获取返回值呢
            Func<int> func = () =>
            {
                Thread.Sleep(2000);
                Console.WriteLine("正在执行有返回值的异步委托");
                Console.WriteLine($"{DateTime.Now.ToString()},threadID=[{Thread.CurrentThread.ManagedThreadId}]");
                return DateTime.Now.Year;
            };
            IAsyncResult asyncResult = func.BeginInvoke(callback, "weiyin");
            //2.IsCompleted完成等待
            int i = 0;
            Console.WriteLine($"Main threadID=[{Thread.CurrentThread.ManagedThreadId}]");
            while (!asyncResult.IsCompleted)
            {
                if (i < 9)
                {
                    Console.WriteLine($"正在玩命加载中。。,已经完成{++i * 10}%");
                }
                else
                {
                    Console.WriteLine($"正在玩命加载中。。,已经完成99.999%");
                }
                Thread.Sleep(200);
            }
            Console.WriteLine("加载完成。。");
            //以上回调函数和IsCompleted 两种都是为了等待任务的完成。
            {
                //3 WaitOne
                asyncResult.AsyncWaitHandle.WaitOne();//一直等待任务完成
                asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待任务完成
                asyncResult.AsyncWaitHandle.WaitOne(3000);//最多等待3000ms,如果超时了,就不等待了
            }
            int iresult = func.EndInvoke(asyncResult);
            Console.WriteLine($"iresult异步执行的委托返回值={iresult}");
        }

 总结:

调用Invoke会同步按顺序执行委托绑定的方法;

调用BeginInvoke单独启动一个线程,异步执行委托绑定的方法,其执行是没有顺序的。

如果想在异步执行委托的同时顺序执行逻辑:可以通过回调函数执行完异步委托绑定的方法的逻辑后,再执行回调函数里面的逻辑。可以给回调函数传参。如果想获取异步委托执行后的返回值,可以通过EndInvoke获取。异步委托执行绑定的方法的相关状态信息存储IAsyncResult信息中。

posted @ 2020-12-25 21:09  liweiyin  阅读(2579)  评论(0编辑  收藏  举报