【问题标题】:Why does this async method lock up the user interface?为什么这个异步方法会锁定用户界面?
【发布时间】:2014-08-23 03:56:57
【问题描述】:

我正在使用以下代码读取网络驱动器上的所有图像并为每个图像填充一个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
  • PopulateImagesasync,但没有 await。这应该会产生一个警告。
  • 你也不应该在这里使用Task.Run,因为你在另一个线程中运行的唯一东西就是你想在UI线程中运行的代码,迫使你不必要地使用Dispatcher,你根本不应该碰它。

标签: c# async-await


【解决方案1】:

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

相反,您应该使用Task.WhenAll,它返回一个Task,它会在所有其他任务完成后自行完成。然后你就可以等待了。

await Task.WhenAll(...);

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

【讨论】:

    【解决方案2】:

    考虑覆盖FrameworkElement.OnInitialized 方法,而不是注册到Loaded 事件。这样,您可以在PopulateImage 上使用await,保存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);
    
    }
    

    【讨论】:

      猜你喜欢
      • 2022-12-17
      • 2021-07-13
      • 1970-01-01
      • 1970-01-01
      • 2016-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-01-03
      相关资源
      最近更新 更多