2016-10-10 23 views
1

は、私はC# https://github.com/mysql/mysql-connector-netC#のMySQLドライバ - 非同期オペレーション最近

が非同期での作業のための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を始めることができます。

当然、クエリで使用された1つまたは複数のメソッドがブロックされていることが示唆されます。 最新のMySQLドライバのソースコード(githubリポジトリから)をダウンロードし、非同期メソッドの実装を探しました。

私はそのクラスを見上げ、私はExecuteReaderAsyncの実装でインスタンスを探し、それがBCL

enter image description here

の一部である基本クラスたSystem.Data.Common.DbCommandに私を導きました

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); 
       } 
      } 
     } 
:.NETリファレンスソース https://referencesource.microsoft.com/#System.Data/System/Data/Common/DBCommand.cs,1875e74763fd9ef2

そして、何私が見たが本当に私を混乱しました

それはすべてがこのラインに沸く:

return Task.FromResult<DbDataReader>(ExecuteReader(behavior)); 

をこの行では、ExecuteReaderのは、同期的に実行し、呼び出し元のスレッドをブロックします。

のExecuteReaderは、抽象メソッドを呼び出しMySQLドライバ内部オーバーライドさ

abstract protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior); 

​​

MySQLの内部実装は基本的にのExecuteReaderの同期バージョンをコール...

つまり、ExecuteReaderAsync()はExecuteReader()を同期して実行し、呼び出しスレッドをブロックします。

私が間違っている場合は私を修正してくださいが、実際にはそうであるようです。

一方

は、MySQLドライバは、アカウントにそれを撮影したはずです

私はまさに、ここにBCLまたはMySQLドライバの実装のたDbCommandクラスを非難するが誰であるかを正確に特定することはできません...、 一方、DbCommandはExecuteDbDataReaderAsyncの基本実装を提供するので、少なくとも実際の非同期I/Oを使用せずにワーカースレッドでExecuteReaderの同期バージョンを起動する必要があります。

どう思いますか?

私は回避策として何ができますか? 自分でタスクとして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コア用のMySQLプロトコルの完全に独立した、完全に非同期の実装です。 APIは公式のMySql.Dataコネクタと同じですので、真の非同期DB接続が必要なほとんどのプロジェクトのドロップイン置換である必要があります。

関連する問題