在异步方法中的C#阻塞代码

6
我正在使用MvvmCross和AsyncEx库在Windows 10 (UWP)应用程序中。在ViewModel中,我有一个INotifyTaskCompletion属性(1),它与ViewModel中的一个异步方法(2)连接起来。
在(2)中,我调用了一个异步库方法,该方法:
  • 检查本地缓存
  • 异步下载数据
  • 将数据添加到缓存中
缓存代码无法异步执行,因此库方法包含阻塞和异步代码。
问:最好的方法是什么,以避免阻塞UI线程?
Stephen Cleary那里了解到,在异步代码中不要阻塞并且不要在库方法中使用Task.Run。所以我必须……
  • 将缓存调用移动到(2)中,例如:
  • 使用Task.Run(检查缓存)
  • 异步调用库方法
  • 再次使用Task.Run(缓存数据)?
是否有更好的方法?

1
你有一些可以分享的代码吗? - Daniel Casserly
根据你对“更好”的定义。如果你不能改变缓存代码并想使用async/await,那么使用Task.Run()来进行缓存代码是“正确”的。 - Jcl
1个回答

8
如果您有完全同步的代码,而无法更改使其返回可等待对象,且想要将其变为异步,则是的,如果您想使用async/await,则唯一的选择是使用Task.Run()。例如:
public async Task<T> MyMethod()
{
   T result = await Task.Run(() => CheckCacheOnSyncMethodICantChange());
   if(result != null)
   {
     result = await MyLibraryMethodThatReturnsATask();
     await Task.Run(() => AddToCacheOnSyncMethodICantChange(result));
   }
   return result;
}

应该没问题。


尽管我已经回答了您的问题,但就您的具体情况而言,缓存应该是快速的(这就是使用它的原因)......您确定需要将缓存方法设置为异步吗? - Jcl
1
我正在使用SQLite.net将数据缓存到基于文件的数据库中。由于这项工作不受CPU限制,我希望在不阻塞UI线程的情况下完成它。我有遗漏了什么吗? - Howard
@Howard 不用担心,只要你的库方法比检查和添加到缓存花费更多时间就可以了。我对SQLite不是很有经验,但如果你正在使用SQLite.Net,我记得在某个地方看到过一个SQLiteAsyncConnection,它有GetAsyncInsertAsync方法。 - Jcl
我认为SQLite.net异步库方法只是在同步库方法周围执行了一个Task.Run,这被一些人描述为反模式。 - Howard
1
@Howard 是的,实际上,Task.Factory.StartNew 看起来是可行的解决方案:https://github.com/oysteinkrog/SQLite.Net-PCL/blob/master/src/SQLite.Net.Async/SQLiteAsyncConnection.cs。唉,没有最好的解决方案。如果你的调用代码(例如,我的回答中的 MyMethod)是一个“调用方法”,而不是一个“库方法”(这应该是的,因为你不想让你的库依赖于一个 SQLite 文件数据库 :-) ),那么使用 Task.Run 就可以。 - Jcl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接