为什么这种异步方法会锁定用户界面
本文关键字:锁定 用户界面 异步方法 为什么 | 更新日期: 2023-09-27 18:19:12
我使用以下代码读取网络驱动器上的所有图像,并用每个图像填充一个ImageControl
,然后将它们显示在屏幕上。
我遇到的问题是,无论PopulateImages()
是否成为async
方法,以及如何Task.WaitAll
运行用户界面,直到所有图像呈现为止,用户界面仍然被锁定。
我做async/await
不正确吗?我需要做什么来解决这个问题?
public MainWindow()
{
InitializeComponent();
Loaded += (s, e) => PopulateImages();
}
private async void PopulateImages()
{
string StartDirectory = @"//path/to/network/folder";
Task.WaitAll(Directory
.EnumerateFiles(StartDirectory)
.Select(filename => Task.Run(async () =>
{
Bitmap resizedImage;
using (var sourceStream = File.Open(filename, FileMode.Open))
{
using (var destinationStream = new MemoryStream())
{
await sourceStream.CopyToAsync(destinationStream);
resizedImage = ResizeImage(new Bitmap(destinationStream), 96, 96);
}
}
Dispatcher.BeginInvoke(new Action(() =>
{
var imgControl = new ImageControl(filename, resizedImage);
stackpanelContainer.Children.Add(imgControl);
}));
})).ToArray());
}
您正在使用Task.WaitAll
- 它会阻止,直到所有任务都完成。
相反,您应该使用 Task.WhenAll
,它返回一个Task
,当所有其他任务完成后,该本身将完成。然后,您可以等待。
await Task.WhenAll(...);
虽然说实话,除非你需要在任务全部完成后做任何事情,否则你根本不需要等待它们。
请考虑重写FrameworkElement.OnInitialized
方法,而不是注册到 Loaded
事件。这样,您可以await
PopulateImage
,保存Task.WaitAll
,并可能消除使用Task.Run
的需要,如果您的ResizeImage
不是太重 CPU :
public override async void OnInitialized()
{
await PopulateImages();
base.OnInitialized();
}
private async Task PopulateImages()
{
string StartDirectory = @"//path/to/network/folder";
Directory.EnumerateFiles(StartDirectory)
.Select(filename => async () =>
{
Bitmap resizedImage;
using (var sourceStream = File.Open(filename, FileMode.Open))
using (var destinationStream = new MemoryStream())
{
await sourceStream.CopyToAsync(destinationStream);
resizedImage = ResizeImage(new Bitmap(destinationStream), 96, 96);
}
}
var imgControl = new ImageControl(filename, resizedImage);
stackpanelContainer.Children.Add(imgControl);
}