在异步程序中,程序代码不需要严格按照编写时的顺序执行
为了改善代码性能,有时候需要在一个新的线程中运行一部分代码
有时候无需创建新的线程,但为了更好的利用单个线程的能力,需要改变代码的执行顺序
也就是说:
异步编程赋予代码非顺序执行的能力,让程序能够在部分耗时操作的同时,干其他的事情
如果委托对象在调用列表中只有一个方法(引用方法),它就可以异步执行这个方法
委托类有 BeginInvoke,EndInvoke 方法,可以用以下方式使用:
使用这一过程有三种标准模式,区别在于:原始线程如何知道发起的线程已经完成
原始线程发起异步方法并做了一些其他处理后,原始线程中断并等待异步方法完成后再继续
原始线程定期检查发起的异步方法线程是否完成,如果没有则继续做其他事情
原始线程一直执行,无需等待,当发起的线程中引用方法完成后,发起的线程就调用回调方法,调用 EndInvoke 之前处理异步方法的结果
1 static void Main(string[] args) 2 { 3 Console.WriteLine("===== 同步调用 ====="); 4 AddDel del = new AddDel(Add); 5 int result = del.Invoke(11, 89); 6 Console.WriteLine("计算结果:" + result); 7 Console.WriteLine("继续削铅笔...\n"); 8 Console.ReadKey(); 9 10 Console.WriteLine("===== 异步调用 ====="); 11 IAsyncResult result_1 = del.BeginInvoke(22, 78, null, null); 12 Console.WriteLine("继续削铅笔..."); 13 Console.WriteLine("计算结果:" + del.EndInvoke(result_1)); 14 Console.ReadKey(); 15 16 Console.WriteLine("\n===== 异步回调 ====="); 17 del.BeginInvoke(33, 67, new AsyncCallback(AddAsync), "AsyncState:OK"); 18 Console.WriteLine("继续削铅笔..."); 19 Console.ReadKey(); 20 } 21 22 // 委托 23 public delegate int AddDel(int a, int b); 24 // 加法计算 25 static int Add(int a, int b) 26 { 27 Console.WriteLine("开始计算:" + a + "+" + b); 28 // 模拟运行时间 29 Thread.Sleep(2000); 30 Console.WriteLine("计算完成!"); 31 return a + b; 32 } 33 // 回调函数 34 static void AddAsync(IAsyncResult ar) 35 { 36 AddDel del = ((AsyncResult)ar).AsyncDelegate as AddDel; 37 Console.WriteLine("计算结果:" + del.EndInvoke(ar)); 38 Console.WriteLine(ar.AsyncState); 39 }
Task 类通常是以异步方式执行的单个操作,更适合在后台完成的一些小任务
由于 Task 对象执行的工作通常在线程池的线程上异步执行,而不是在程序主线程上同步执行
因此可以使用 Status 属性,还可以使用 IsCancele、IsCompleted 和 IsFaulted 属性来确认任务的状态
大多数情况下,lambda 表达式用于指定的任务是执行的工作
:
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程正在执行业务处理!"); 4 // 创建任务 5 Task task = new Task(() => 6 { 7 Console.WriteLine("使用Task执行异步操作:"); 8 for (int i = 0; i <= 10; i++) 9 { 10 Console.WriteLine("操作执行:" + i * 10 + "%"); 11 Thread.Sleep(100); 12 } 13 Console.WriteLine("Task异步操作执行完成!"); 14 }); 15 // 启动任务,并安排到当前任务队列线程中执行任务 16 task.Start(); 17 Thread.Sleep(500); 18 Console.WriteLine("主线程正在执行其他业务!"); 19 Console.ReadKey(); 20 }
C# 中的 Async 和 Await 关键字是异步编程的核心
通过这两个关键字,可以使用 .NET Framework、.NET Core 或 Windows 运行时中的资源,轻松创建异步方法(几乎与创建同步方法一样轻松)
而使用 Async 关键字定义的异步方法简称为 "异步方法"
:
1 static void Main(string[] args) 2 { 3 Console.WriteLine("主线程正在执行第一项业务!"); 4 AsyncTest(); 5 Thread.Sleep(300); 6 Console.WriteLine("主线程正在执行第二项业务!"); 7 Thread.Sleep(300); 8 Console.WriteLine("主线程正在执行第三项业务!"); 9 Thread.Sleep(500); 10 Console.WriteLine("主线程正在执行最后一项业务!"); 11 Console.ReadKey(); 12 } 13 14 public static async void AsyncTest() 15 { 16 Console.WriteLine("开始执行异步操作:"); 17 // await 不会开启新的线程 18 // 需要自己创建Task,才会真正的去创建线程 19 //await new Program().AsyncTaskFun(); 20 await Task.Run(() => 21 { 22 new Program().AsyncTaskFun(); 23 }); 24 Console.WriteLine("异步操作执行完成!"); 25 } 26 27 public async TaskAsyncTaskFun() 28 { 29 Console.WriteLine("异步操作正在执行:"); 30 for (int i = 1; i <= 10; i++) 31 { 32 Console.WriteLine("操作执行:" + i * 10 + "%"); 33 Thread.Sleep(100); 34 } 35 return 2; 36 }
有时候我们需要在后台新建一个线程默默完成一项工作,过程中时不时同主线程进行通信,这就是 BackgroundWorker 的主要任务
BackgroundWorker 类允许在单独的线程上执行某个可能导致用户界面(UI)停止响应的耗时操作,并且想要一个响应式的UI来反应当前耗时操作的进度
:
1 public partial class Form1 : Form 2 { 3 // BackgroundWorker实例 4 BackgroundWorker bw = new BackgroundWorker(); 5 6 public Form1() 7 { 8 InitializeComponent(); 9 bw.WorkerReportsProgress = true; 10 bw.WorkerSupportsCancellation = true; 11 bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged); 12 bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 13 bw.DoWork += new DoWorkEventHandler(bw_DoWork); 14 progressBar1.Maximum = 100; 15 } 16 // 进度条变化 17 private void bw_ProgressChanged(Object sender, ProgressChangedEventArgs e) 18 { 19 progressBar1.Value = e.ProgressPercentage; 20 label1.Text = e.UserState.ToString(); 21 label1.Update(); 22 } 23 // 后台任务完成 24 private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 25 { 26 MessageBox.Show("后台操作执行完成!"); 27 } 28 // 后台具体任务 29 private void bw_DoWork(object sender, DoWorkEventArgs e) 30 { 31 BackgroundWorker bw = sender as BackgroundWorker; 32 int num = 101; 33 for (int i = 0; i < num; i++) 34 { 35 if (bw.CancellationPending) 36 { 37 bw.ReportProgress(i, $"当前进度{i}%, 已停止!"); 38 return; 39 } 40 bw.ReportProgress(i, $"当前进度{i}%"); 41 Thread.Sleep(100); 42 } 43 return; 44 } 45 // 开始按钮 46 private void button1_Click(object sender, EventArgs e) 47 { 48 if (bw.IsBusy) return; 49 bw.RunWorkerAsync(); 50 } 51 // 停止按钮 52 private void button2_Click(object sender, EventArgs e) 53 { 54 bw.CancelAsync(); 55 } 56 }