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; } }
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信息中。