Dapper QueryAsync是否阻止首次查询(针对Oracle服务器)的UI? [英] Dapper QueryAsync blocks UI for the first time querying (against Oracle server)?
问题描述
首先,我认为第一次只是更清楚地看到这种阻塞的一种条件。对于下一次,它仍然以某种方式阻止用户界面轻微,但不像不使用异步时那样明显。
我之所以这样说,是因为我可以看出使用QueryAsync
和使用Task.Run(() => connection.Query<T>)
的简单包装代码之间的区别。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
),请分享您的答案。
推荐答案
使用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线程。
有关详细信息,请查看:
- Should I expose asynchronous wrappers for synchronous methods?Stephen Toub的文章
- Async/Await FAQStephen Toub的文章
- Task.Run Etiquette Examples: Even in the Complex Case, Don't Use Task.Run in the ImplementationStephen Cleary的文章
这篇关于Dapper QueryAsync是否阻止首次查询(针对Oracle服务器)的UI?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!