方式1:async/await模式
private async void button1_Click(object sender, EventArgs e){try{var result = await Task.Run(() => CalculateResult());label1.Text = result.ToString();}catch (Exception ex){label1.Text = $"Error: {ex.Message}";}}private int CalculateResult(){System.Threading.Thread.Sleep(3000); // 模拟耗时计算Random r = new Random();int a = r.Next(1, 100);int b = r.Next(1, 100);return a + b;}
方式2:回调注册模式
private void button1_Click(object sender, EventArgs e){var ts = RunAsyncTask();RegisterCallback(ts, label1);}private Task<int> RunAsyncTask(){return Task.Run(() =>{System.Threading.Thread.Sleep(3000); // 模拟耗时计算Random r = new Random();int a = r.Next(1, 100);int b = r.Next(1, 100);return a + b;});}private void RegisterCallback(Task<int> task,Label label){task.GetAwaiter().OnCompleted(() =>{label.Invoke((MethodInvoker)delegate{label.Text = $"result: {task.Result}";});});}
简化写法(方式2):
private void button1_Click(object sender, EventArgs e){RunAsyncTask().ContinueWith(t =>{label1.Invoke(new Action(() =>{label1.Text = t.IsFaulted ?$"Error: {t.Exception?.InnerException?.Message}" :$"Result: {t.Result}";}));}, TaskScheduler.Default);}private Task<int> RunAsyncTask(){return Task.Run(() =>{System.Threading.Thread.Sleep(3000); // 模拟耗时计算Random r = new Random();int a = r.Next(1, 100);int b = r.Next(1, 100);return a + b;});}
二、核心机制分析
1. 共同点
-
都使用
Task.Run
将耗时操作放到线程池执行 -
都能保持UI线程的响应性
-
最终都能正确更新UI控件
2. 关键差异
特性 | async/await模式 | 回调注册模式 |
---|---|---|
代码结构 | 线性流程,更易读 | 分散式,逻辑分离 |
线程上下文恢复 | 自动恢复UI上下文 | 需手动Invoke回UI线程 |
异常处理 | 可直接用try-catch | 需在回调中处理异常 |
可扩展性 | 相对固定 | 更灵活,可自定义回调逻辑 |
适用场景 | 简单异步操作 | 需要自定义完成逻辑的复杂场景 |
代码维护性 | 高,逻辑集中 | 较低,逻辑分散 |
调试难度 | 较低,有完整的调用栈 | 较高,调试异步回调较复杂 |
三、性能与资源比较
-
内存分配:
-
async/await会生成状态机,有额外分配
-
回调模式更直接,分配更少
-
-
执行效率:
-
实际计算性能几乎无差别
-
回调模式在极高并发下可能略有优势
-
-
线程使用:
-
两种方式都正确使用线程池
-
无实质性差异
-
四、最佳实践建议
优先使用async/await模式的情况:
-
简单的异步操作
-
需要清晰的代码流程
-
需要直接处理异常
-
团队对async/await更熟悉时
考虑使用回调模式的情况:
-
需要自定义任务完成后的处理逻辑
-
需要更精细控制任务生命周期
-
在性能敏感的极高并发场景
-
需要复用相同的回调逻辑时