Dapper QueryAsync首次阻止UI(针对Oracle服务器)吗?

时间:2018-08-02 04:15:19

标签: c# asynchronous dapper blocking

首先,我相信 第一次 只是一个条件,可以更清楚地看到此阻止。对于下一次,它仍然会 以某种方式阻塞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),请分享您的答案。

2 个答案:

答案 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?