2016-10-10 89 views
1

最近我已經開始與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

enter image description here

的一部分,我擡頭一看那個類的.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作爲一項任務,但我不喜歡這個解決方案。

你有什麼建議?

謝謝, 阿里克

回答

3

DbCommand類自(至少).NET 2.0一直圍繞。當Microsoft在.NET 4.5中添加了ExecuteNonQueryAsync,ExecuteReaderAsync等方法時,他們必須以向後兼容的方式進行操作。

執行此操作的最佳方法是執行.NET框架的工作:委託給現有的同步方法並將其返回值包裝在Task中。 (這是幾乎從來沒有一個好主意,通過調用執行Task.Run製作方法「異步」;對於更詳細的說明,請參閱Should I expose asynchronous wrappers for synchronous methods?Task.Run Etiquette and Proper Usage。)

要獲得真正的異步行爲,數據庫連接庫的開發必須將其轉換爲真正的異步。這可能很困難;使大型同步代碼庫異步可能涉及重寫大部分代碼。

目前,Oracle的用於.NET的MySQL連接器並未實現真正的異步方法。 MySQL Bug 70111在MySQL連接器中報告此問題。這也在this question中進一步討論。

我會推薦使用我一直在使用的庫:MySqlConnector on NuGetGitHub。它是.NET和.NET Core的完全獨立的,完全異步的MySQL協議實現。該API與官方的MySql.Data連接器相同,因此它應該是大多數項目(需要真正的異步數據庫連接)的直接替代品。