最近我已經開始與MySQL驅動工作了C# https://github.com/mysql/mysql-connector-netC#MySQL驅動程序 - 異步操作
與異步工作/等待我試圖在並行任務
運行簡單的SELECT查詢,這基本上是如何代碼如下:
private async Task<List<string>> RunQueryA()
{
List<string> lst = new List<string>();
using (MySqlConnection conn = new MySqlConnection(someConnectionString))
using (MySqlCommand cmd = conn.CreateCommand())
{
await conn.OpenAsync();
cmd.CommandText = "select someField from someTable ...";
using (var reader = await cmd.ExecuteReaderAsync())
{
// ...
}
}
return lst;
}
private async Task<List<string>> RunQueryB()
{
List<string> lst = new List<string>();
using (MySqlConnection conn = new MySqlConnection(someConnectionString))
using (MySqlCommand cmd = conn.CreateCommand())
{
await conn.OpenAsync();
cmd.CommandText = "select someField2 from someTable2 ...";
using (var reader = await cmd.ExecuteReaderAsync())
{
// ...
}
}
return lst;
}
public async Task Run()
{
await Task.WhenAll(RunQueryA(), RunQueryB());
}
我所期待的是爲這兩個查詢並行運行,我所看到的是,RunQueryA()開始運行,只有一次有人做過RunQueryB可以開始了。
當然,它會暗示在查詢中使用的一個或多個方法是阻塞的。 爲了找到答案,我下載了最新的MySQL驅動程序源代碼(從他們的github回購),並尋找異步方法的實現。
我看了例如在ExecuteReaderAsync的實現,它使我的基類System.Data.Common.DbCommand這是BCL
的一部分,我擡頭一看那個類的.NET參考源 https://referencesource.microsoft.com/#System.Data/System/Data/Common/DBCommand.cs,1875e74763fd9ef2
我所看到的真糊塗我:
public Task<DbDataReader> ExecuteReaderAsync() {
return ExecuteReaderAsync(CommandBehavior.Default, CancellationToken.None);
}
public Task<DbDataReader> ExecuteReaderAsync(CancellationToken cancellationToken) {
return ExecuteReaderAsync(CommandBehavior.Default, cancellationToken);
}
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior) {
return ExecuteReaderAsync(behavior, CancellationToken.None);
}
public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) {
return ExecuteDbDataReaderAsync(behavior, cancellationToken);
}
protected virtual Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) {
if (cancellationToken.IsCancellationRequested) {
return ADP.CreatedTaskWithCancellation<DbDataReader>();
}
else {
CancellationTokenRegistration registration = new CancellationTokenRegistration();
if (cancellationToken.CanBeCanceled) {
registration = cancellationToken.Register(CancelIgnoreFailure);
}
try {
return Task.FromResult<DbDataReader>(ExecuteReader(behavior));
}
catch (Exception e) {
registration.Dispose();
return ADP.CreatedTaskWithException<DbDataReader>(e);
}
}
}
這一切都歸結到這條線:
return Task.FromResult<DbDataReader>(ExecuteReader(behavior));
在這一行中的ExecuteReader將同步運行,並阻止調用線程。
的ExecuteReader調用一個抽象方法
abstract protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior);
這是MySQL驅動內覆蓋:
MySQL的內部實現基本呼叫的ExecuteReader的同步版本...
簡而言之,ExecuteReaderAsync()同步運行ExecuteReader()並阻塞調用線程。
請糾正我,如果我錯了,但它確實似乎是這樣的。
我不能找出究竟是誰在這裏怪的的DbCommand類的BCL或MySQL驅動程序實現的...
一方面,MySQL驅動程序應該已經把它做爲考慮, 另一方面,由於DbCommand提供了ExecuteDbDataReaderAsync的基本實現,它至少應該在工作線程中啓動同步版本的ExecuteReader(更不用說使用實際的異步I/O了),因此它不會阻塞。
想什麼呢?
作爲解決方法,我該怎麼做? 我可以直接啓動ExecuteReaderAsync作爲一項任務,但我不喜歡這個解決方案。
你有什麼建議?
謝謝, 阿里克