了解async/await和Task.Run()

Tob*_*ayn 20 c# asynchronous task async-await xamarin

我想我明白了async/ awaitTask.Run()相当好,直到我来到这个问题:

我编程使用一个Xamarin.Android应用RecyclerViewViewAdapter.在我的OnBindViewHolder方法中,我尝试异步加载一些图像

public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
    // Some logic here

    Task.Run(() => LoadImage(postInfo, holder, imageView).ConfigureAwait(false)); 
}
Run Code Online (Sandbox Code Playgroud)

然后,在我的LoadImage函数中,我做了类似的事情:

private async Task LoadImage(PostInfo postInfo, RecyclerView.ViewHolder holder, ImageView imageView)
{                
    var image = await loadImageAsync((Guid)postInfo.User.AvatarID, EImageSize.Small).ConfigureAwait(false);
    var byteArray = await image.ReadAsByteArrayAsync().ConfigureAwait(false);

    if(byteArray.Length == 0)
    {
        return;
    }

    var bitmap = await GetBitmapAsync(byteArray).ConfigureAwait(false);

    imageView.SetImageBitmap(bitmap);
    postInfo.User.AvatarImage = bitmap;
}
Run Code Online (Sandbox Code Playgroud)

这段代码有效.但为什么?

我所学到的,在configure await设置为false之后,代码不会在SynchronizationContext(它是UI线程)中运行.

如果我使OnBindViewHolder方法异步并使用await而不是Task.Run,​​则代码崩溃

imageView.SetImageBitmap(bitmap);
Run Code Online (Sandbox Code Playgroud)

说它不在UI线程中,这对我来说非常有意义.

那么为什么async/ awaitcode会在Task.Run()没有崩溃的情况下崩溃呢?

更新:答案

由于没有等待Task.Run,​​因此未显示抛出的异常.如果我等待Task.Run,​​那就是我预料到的错误.进一步的解释见下面的答案.

Den*_*öer 10

Task.Run() UI线程应该用于不同的目的:

  • Task.Run()应该用于CPU绑定方法.
  • UI-Thread应该用于UI相关的方法.

通过移动代码Task.Run(),可以避免阻止UI线程.这可能会解决您的问题,但这不是最佳做法,因为它对您的表现有害.Task.Run()阻塞线程池中的线程.

你应该做的是在UI线程上调用你的UI相关方法.在Xamarin中,您可以使用Device.BeginInvokeOnMainThread()以下命令在UI线程上运行:

// async is only needed if you need to run asynchronous code on the UI thread
Device.BeginInvokeOnMainThread(async () =>
{
    await LoadImage(postInfo, holder, imageView).ConfigureAwait(false)
});
Run Code Online (Sandbox Code Playgroud)

为什么它的工作,即使你不显式调用它的UI线程的原因可能是因为Xamarin莫名其妙地检测到它应该在UI线程上运行的东西和转移这项工作到UI线程.

以下是Stephen Cleary的一些有用的文章,它帮助我写了这个答案,它将帮助您进一步理解异步代码:

https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html https://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-using.html


Rob*_*bin 8

它就像你没有等待Task.Run一样简单,所以异常会被吃掉而不会返回到Task.Run的调用站点.

在Task.Run前面添加"await",你就会得到异常.

这不会导致您的应用程序崩溃:

private void button1_Click(object sender, EventArgs e)
{
    Task.Run(() => { throw new Exception("Hello");});
}
Run Code Online (Sandbox Code Playgroud)

但是这会使您的应用程序崩溃:

private async void button1_Click(object sender, EventArgs e)
{
   await Task.Run(() => { throw new Exception("Hello");});
}
Run Code Online (Sandbox Code Playgroud)