基于委托的异步编程模型是 .NET 早期版本中实现异步操作的一种方式,主要通过 BeginInvoke
和 EndInvoke
方法来实现。这种基于委托的异步模式已被 Task
和 async/await
模式取代,但在维护旧代码时仍可能遇到这种模式。
委托的方法中:Invoke用于同步调用; 而BeginInvoke与EndInvoke用于异步调用。
一、具体介绍:
1.1 异步调用BeginInvoke 方法:
调用格式是:BeginInvoke( 委托的参数,AsyncCallback类型的回调函数,一个object对象), 该回调函数是在委托完成后自动调用的函数。 object对象是任何一种对象,可以传递给委托函数。
特点如下:
a. 主线程调用此函数后,立即返回,不等待调用完成
b. 返回 IAsyncResult 用于监视调用状态. 在EnInvoke中使用该返回值
1.2 EndInvoke 方法:
用于获取异步调用的结果;调用此方法后,如果操作未完成,会阻塞调用线程直到完成。
而且:如果委托函数中有exception异常,是在调用EndInvoke时触发这个异常的。即即使委托执行中有异常,如果后面没有调用EndInvoke这个异常不会被程序捕获到。
使用方法如下:
a = delfun.BeginInvoke(....)
结果 = delfun.EndInvoke(a).
1.3 回调函数:
当异步操作完成时自动调用; 一般在回调函数中调用 EndInvoke得到结果。
1.4 IAsyncResult 对象
属性: IsCompleted 委托是否完成(包括正常完成、异常完成)
委托:AsyncDelegate
传入的参数: AsyncState,即调用beginInvoke时传入的最后一个参数,若不试用,可以是null;
二、实际案例如下:
本案例包含委托的异步调用、回调函数,得到结果,以及委托的取消。
public partial class BackgroundDemo : Window{delegate int CalculateDelegate(int x, int y, CancellationToken ct);CancellationTokenSource cts = null;public BackgroundDemo(){InitializeComponent(); btnCancel.IsEnabled = false;cts = new CancellationTokenSource();}int CalculateSum(int a, int b, CancellationToken ct){Console.WriteLine("计算开始子线程ID: " + Thread.CurrentThread.ManagedThreadId);for (int i = 0; i < 20; i++){ct.ThrowIfCancellationRequested(); Thread.Sleep(1000);} return a + b;}void CalculationComplete(IAsyncResult result){CalculateDelegate calculate = (CalculateDelegate)result.AsyncState;var arResult = result as AsyncResult;try{int sum = calculate.EndInvoke(result);Console.WriteLine("回调中获取结果: " + sum + " (线程ID: " + Thread.CurrentThread.ManagedThreadId + ")");}catch(OperationCanceledException ex){Console.WriteLine("回调时发现:任务取消: " + " (线程ID: " + Thread.CurrentThread.ManagedThreadId + ")");}this.Dispatcher.BeginInvoke((Action)delegate{this.btnStart.IsEnabled = true;});Console.WriteLine("------------结束----------------");}private void btnStart_Click(object sender, RoutedEventArgs e){this.btnStart.IsEnabled = false;this.btnCancel.IsEnabled = true;// 如果需要重置,首先取消当前的令牌cts.Cancel();// 然后释放资源cts.Dispose();// 重新创建 CancellationTokenSourcects = new CancellationTokenSource();this.tbInfo.Text = "开始计算中";Console.WriteLine("------------开始------------");Console.WriteLine("UI线程ID: " + Thread.CurrentThread.ManagedThreadId );CalculateDelegate cal = CalculateSum;cal.BeginInvoke(4, 5,cts.Token, CalculationComplete, cal);}private void btnCancel_Click(object sender, RoutedEventArgs e){this.btnCancel.IsEnabled = false;cts.Cancel();}
}