Tob*_*ayn 20 c# asynchronous task async-await xamarin
我想我明白了async/ await和Task.Run()相当好,直到我来到这个问题:
我编程使用一个Xamarin.Android应用RecyclerView了ViewAdapter.在我的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绑定方法.通过移动代码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
它就像你没有等待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)