2016-10-10 119 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连接器相同,因此它应该是大多数项目(需要真正的异步数据库连接)的直接替代品。