为什么这个异步方法会锁定用户界面?

时间:2014-08-22 16:43:24

标签: c# async-await

我使用以下代码读取网络驱动器上的所有图像,并在每个图像上弹出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());
}

2 个答案:

答案 0 :(得分:8)

您正在使用Task.WaitAll - 阻止,直到完成所有任务。

相反,您应该使用Task.WhenAll,它会返回Task,当所有其他任务完成后,它将自动完成。然后你可以等待它。

await Task.WhenAll(...);

虽然说实话,除非你在任务完成后需要做任何事情,否则你根本不需要等待它们。

答案 1 :(得分:0)

不要注册Loaded事件,而应考虑覆盖FrameworkElement.OnInitialized方法。这样,如果您的await不太重,那么您可以在PopulateImageTask.WaitAll保存Task.Run并可能无需使用ResizeImage

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);

}