为什么使用await关键字的异步方法仍然阻塞主线程?

Iva*_*azz 1 c# task async-await

任何人都可以向我解释这两种异步方法之间的区别吗?

方法A.

public async Task<List<Thumbnail>> GetAllThumbnailsAsync()
{
    return await Task.Factory.StartNew(() =>             
    {
        var imageUris = GetAllDirectoriesWithImageAsync(CommandBaseUri).Result;

        return imageUris.Select(GetThumbnail).OrderByDescending(t => t.ImageDateTime).ToList();
    });
}
Run Code Online (Sandbox Code Playgroud)

方法B.

public async Task<List<Thumbnail>> GetAllThumbnailsAsync()
{
    var imageUris = await GetAllDirectoriesWithImageAsync(CommandBaseUri);

    return imageUris.Select(GetThumbnail).OrderByDescending(t => t.ImageDateTime).ToList();           
}
Run Code Online (Sandbox Code Playgroud)

据我所知,这两个方法都应该返回调用者并且不会阻止UI线程,但在这种情况下,只有方法A按预期工作,而方法B阻止我的UI.

我相信必须有一些基本概念,我可能在使用async/await时误解了.

任何人都可以开导我吗?

Jon*_*eet 5

方法A基本上是在一个单独的任务中执行所有操作,这可能最终会出现在一个新线程中.当您等待结果任务时,您不会阻止任何事情.

方法B首先调用GetAllDirectoriesWithImageAsync,然后等待结果.这意味着在异步操作处理时,您不会阻止UI线程 - 但默认情况下,当您等待任务时,这意味着继续将在UI线程中运行.因此,imageUris.Select(...).OrderByDescending(...).ToList()将在UI线程中运行,我怀疑这是造成在UI问题的一部分.

现在,你可以调用.ConfigureAwait(false)在第一线的末端GetAllThumbnailsAsync,以表明你没有需要执行的UI线程上的第二部分-但将不能保证你不会在UI线程上执行的第二部分.相反,在我看来,你真的想要一种获取缩略图的异步方式.然后你可以这样做:

public async Task<List<Thumbnail>> GetAllThumbnailsAsync()
{
    var imageUris = await GetAllDirectoriesWithImageAsync(CommandBaseUri)
        .ConfigureAwait(false);

    var thumbnailTasks = imageUris.Select(GetThumbnailAsync).ToList();
    var thumbnails = await Task.WhenAll(thumbnailTasks).ConfigureAwait(false);

    // I'm assuming there will be sufficiently few thumbnails here
    // that sorting them on the UI thread wouldn't be a problem, in
    // the unlikely event that we're *really* still on the UI thread by
    // this point...
    return thumbnails.OrderByDescending(t => t.ImageDateTime).ToList();           
}
Run Code Online (Sandbox Code Playgroud)

请注意,我假设所有异步方法都是合理编写的,例如,GetAllDirectoriesWithImageAsync在返回任务之前不会执行大量的同步工作.