首先,我相信 第一次 只是一个条件,可以更清楚地看到此阻止。对于下一次,它仍然会 以某种方式阻塞UI,但不会像不使用异步时那样明显。
我可以这样说,因为我可以看到使用QueryAsync
与使用Task.Run(() => connection.Query<T>)
进行简单包装的代码之间的区别,该代码可以正常工作,并且当然比QueryAsync
更好(在UX中)
这样的代码很简单:
public async Task<IEnumerable<Item>> LoadItemsAsync(){
using(var con = new OracleConnection(connectionString)){
var items = await con.QueryAsync<dynamic>("someQuery");
return items.Select(e => new Item { ... });
}
}
//in UI thread, load items like this:
var items = await LoadItemsAsync();
可以正常工作的代码(不阻塞UI)是这样的:
public async Task<IEnumerable<Item>> LoadItemsAsync(){
using(var con = new OracleConnection(connectionString)){
var items = await Task.Run(() => con.Query<dynamic>("someQuery"));
return items.Select(e => new Item { ... });
}
}
//in UI thread, load items like this:
var items = await LoadItemsAsync();
我知道Task.Run()
实际上不是与细节异步的,但是至少可以将整个工作放在另一个线程中,并使UI不受阻塞和冻结。
我猜这可能是Dapper
中的错误,请花一些时间进行测试。我不确定如何准确地重现此信息,但是如果可能,请尝试使用Winforms project
,一个相当大的Oracle数据库,当然,正如我所说的,您可以在第一次查询时最明显地看到它(因此,确保在每次测试之前对Oracle服务器运行清除缓存查询。
最后,如果您对此有一些解释和解决方案(当然不使用Task.Run
),请分享您的答案。
答案 0 :(得分:6)
使用async await
只能在执行真正的异步操作(例如,异步IO或委派给线程池线程的任务)的过程中释放和使用UI线程。就您而言,利用Oracle驱动程序(ODP.NET)的方法并不是真正的异步方法。请参阅Can the Oracle Managed Driver use async/wait properly?关于堆栈溢出的讨论。
如果您想从UI线程中卸载工作以提高响应速度,只需使用Task.Run()
:
var items = await Task.Run(() => LoadItems());
使用任何其他机制(例如Task.ConfigureAwait(false)
或将同步上下文替换与Task.Yield()
组合在一起)也将导致使用附加的线程池线程,但是稍后将释放UI线程。
有关更多信息,请检查:
答案 1 :(得分:0)
我认为您在此处看到的内容可能是由于.net中的错误,而不是精致的。该错误是:
https://github.com/Microsoft/dotnet/issues/579
您可能看到的问题是dapper.Query将在内部调用SqlDataReader.ReadAsync,这有时会导致同步/阻止行为。该错误将在.net 4.7.3中修复,但是最新的预发行版本似乎仍然包含此问题。
另请参阅:How do I make SqlDataReader.ReadAsync() run asynchronously?