外观
WPF中的各种异步
1.正常异步
private async void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 3; i++)
{
Debug.WriteLine($"=={i}.==");
await DoWorkAsync(i);
Debug.WriteLine($"=={i}.end==");
}
}
public async Task DoWorkAsync(int i)
{
Debug.WriteLine(i + "开始工作");
await Task.Delay(1000); // 模拟异步操作
Debug.WriteLine(i + "工作完成");
}
/***输出:****
==0.==
0开始工作
0工作完成
==0.end==
==1.==
1开始工作
1工作完成
==1.end==
==2.==
2开始工作
2工作完成
==2.end==
*************/按部就班执行返回,因为Task.Delay(1000)异步非阻塞,UI不会阻塞
2.定义一个不需要等待结果回来的异步 (fire-and-forget)
调用了就不管 缺点:不知道是否执行完成,无法捕获异常或者获取返回值
private void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 3; i++)
{
Debug.WriteLine($"=={i}.==");
DoWorkAsync(i);
Debug.WriteLine($"=={i}.end==");
}
}
public async Task DoWorkAsync(int i)
{
Debug.WriteLine(i + "开始工作");
await Task.Delay(1000); // 模拟异步操作
Debug.WriteLine(i + "工作完成");
}
/***输出:****
==0.==
0开始工作
==0.end==
==1.==
1开始工作
==1.end==
==2.==
2开始工作
==2.end==
1工作完成
2工作完成
0工作完成
*************/3.死锁示例
private void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 3; i++)
{
Debug.WriteLine($"=={i}.==");
var r = DoWorkAsync(i);
var result = r.Result;
//或者 DoWorkAsync(i).Wait();都会造成死锁
Debug.WriteLine($"=={i}.end==");
}
}
public async Task<int> DoWorkAsync(int i)
{
Debug.WriteLine(i + "开始工作");
await Task.Delay(1000); // 模拟异步操作
Debug.WriteLine(i + "工作完成");
return i * 2;
}解析为什么会造成死锁 因为DoWorkAsync依赖于UI线程,UI线程需要处理消息和界面,wait()或Result都是同步阻塞的方法占用UI线程,会等待DoWorkAsync()执行完成后释放,DoWorkAsync的Task.Delay虽然本身不依赖UI线程,但是他await后会尝试在UI线程上继续执行,所以会造成互相等待释放,死锁! 解决:1不使用wait()或Result,2让Task.Delay不依赖UI线程Task.Delay(1000).ConfigureAwait(false)
3.1 死锁解决与执行线程
直观的添加执行的线程的id
List<Task> tasks = new List<Task>();
public Action<int, int> ResultNoti = (iii, rrr) => Debug.WriteLine("通知结果 " + iii + "->" + rrr);
private async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
for (int i = 0; i < 7; i++)
{
// 在 UI 线程启动
Debug.WriteLine("UI 线程ID: " + Environment.CurrentManagedThreadId);
// 模拟长时间运行的任务(不阻塞 UI)
int result = await DoWorkAsync(i).ConfigureAwait(false);
// 注意:这里不会自动回到 UI 线程!
Debug.WriteLine("当前线程ID: " + Environment.CurrentManagedThreadId);
Debug.WriteLine("结果: " + result);
// 如果需要更新 UI,必须手动调度回 UI 线程
Dispatcher.Invoke(() =>
{
Debug.WriteLine("Dispatcher 线程ID: " + Environment.CurrentManagedThreadId);
this.Content = result.ToString();
});
}
}
catch (Exception ex)
{
Debug.WriteLine("发生异常: " + ex.Message);
}
}
public async Task<int> DoWorkAsync(int i)
{
Debug.WriteLine(i + "开始工作");
await Task.Delay(1000).ConfigureAwait(false); //模拟异步操作
Debug.WriteLine(i + " 工作完成");
int r = i * 2;
ResultNoti.Invoke(i, r);
return r;
}
/***
UI 线程ID: 1
0开始工作
0 工作完成
通知结果 0->0
当前线程ID: 9
结果: 0
Dispatcher 线程ID: 1
UI 线程ID: 9
1开始工作
1 工作完成
通知结果 1->2
当前线程ID: 9
结果: 2
Dispatcher 线程ID: 1
UI 线程ID: 9
2开始工作
2 工作完成
通知结果 2->4
当前线程ID: 9
结果: 4
Dispatcher 线程ID: 1
UI 线程ID: 9
3开始工作
3 工作完成
通知结果 3->6
当前线程ID: 9
结果: 6
Dispatcher 线程ID: 1
UI 线程ID: 9
4开始工作
4 工作完成
通知结果 4->8
当前线程ID: 9
结果: 8
Dispatcher 线程ID: 1
UI 线程ID: 9
5开始工作
5 工作完成
通知结果 5->10
当前线程ID: 9
结果: 10
Dispatcher 线程ID: 1
UI 线程ID: 9
6开始工作
*****/可以看到UI线程的id为1,线程池的id为9; 为什么除了第一次的UI线程为1 后面都是9,因为在await Task.Delay(1000).ConfigureAwait(false)后 await的代码都不会回到UI线程执行了都是在线程池的线程执行所以id都是9,但是明显UI线程的id是1,通过Dispatcher.Invoke可以明显看出来
4.添加完成监听
public Action<int, int> ResultNoti= (iii, rrr) => Debug.WriteLine("通知结果 " + iii + "->" + rrr);
private void Button_Click(object sender, RoutedEventArgs e)
{
for (int i = 0; i < 3; i++)
{
Debug.WriteLine($"=={i}.==");
DoWorkAsync(i);
Debug.WriteLine($"=={i}.end==");
}
}
public async Task<int> DoWorkAsync(int i)
{
Debug.WriteLine(i + "开始工作");
await Task.Delay(1000); //模拟异步操作
Debug.WriteLine(i + " 工作完成");
int r = i * 2;
ResultNoti.Invoke(i, r);
return r;
}
/***输出:****
==0.==
0开始工作
==0.end==
==1.==
1开始工作
==1.end==
==2.==
2开始工作
==2.end==
0 工作完成
通知结果 0->0
1 工作完成
通知结果 1->2
2 工作完成
通知结果 2->4
*************/4.1 进度报告IProgress
private async void StartProcessingButton_Click(object sender, RoutedEventArgs e)
{
var progress = new Progress<int>(percent =>
{
ProgressBar.Value = percent;
ProgressText.Text = $"{percent}%";
});
await ProcessDataAsync(progress);
}
public async Task ProcessDataAsync(IProgress<int> progress)
{
for (int i = 0; i <= 100; i += 10)
{
await Task.Delay(300); // 模拟耗时操作
progress?.Report(i); // 自动回到 UI 线程更新
}
}Progress<T>内部使用的SynchronizationContext同步上下文,保证回调在UI线程执行
5.优化方案
public Action<int, int> ResultNoti= (iii, rrr) => Debug.WriteLine("通知结果 " + iii + "->" + rrr);
private async void Button_Click(object sender, RoutedEventArgs e)
{
var tasks = new List<Task<int>>();
for (int i = 0; i < 3; i++)
{
Debug.WriteLine($"=={i}.==");
tasks.Add(DoWorkAsync(i));
Debug.WriteLine($"=={i}.end==");
}
int[] ints = await Task.WhenAll(tasks);
Debug.WriteLine("所有任务完成");
for (int i = 0; i < ints.Length; i++)
{
Debug.WriteLine($"{ints[i]}");
}
}
public async Task<int> DoWorkAsync(int i)
{
Debug.WriteLine(i + "开始工作");
await Task.Delay(1000); //模拟异步操作
Debug.WriteLine(i + " 工作完成");
int r = i * 2;
ResultNoti.Invoke(i, r);
return r;
}
/*******
==0.==
0开始工作
==0.end==
==1.==
1开始工作
==1.end==
==2.==
2开始工作
==2.end==
2 工作完成
通知结果 2->4
0 工作完成
通知结果 0->0
1 工作完成
通知结果 1->2
所有任务完成
0
2
4
**************/