MySQL C#异步方法不起作用?

我在服务器中有一个数据库,似乎异步方法不起作用。

这是我的代码:

static async void Example() { string connectionString = "Server=mydomainname.com;" + "Port=3306;" + "Database=scratch;" + "Uid=Assassinbeast;" + "Password=mypass123;" + "AllowUserVariables= true;"; MySql.Data.MySqlClient.MySqlConnection sqConnection = new MySql.Data.MySqlClient.MySqlConnection(connectionString); await sqConnection.OpenAsync(); Console.Write("Opened. Now Click to close"); Console.ReadLine(); sqConnection.Close(); } static void Main(string[] args) { Console.ReadLine(); Example(); Console.WriteLine("Done"); Console.ReadLine(); } 

在“await”语句中,它实际上应该跳回Main()函数并写出“Done”。 但它不这样做。 它只是同步运行,就像它不是异步方法一样,一旦function完全完成,它将首先写入“完成”。

那么我做错了什么? 这是一个错误吗?

UPDATE

好的,所以在我得到一些答案后,我实际上仍然看不到OpenAsync()和Open()之间的任何区别。

我开始尝试测试更多的东西,我想我可以得出结论,异步方法不起作用

这是我的新代码:

 static async Task Example() { string connectionString = "Server=mydomainname.com;" + "Port=3306;" + "Database=scratch;" + "Uid=Assassinbeast;" + "Password=mypass123;" + "AllowUserVariables= true;"; using (var sqConnection = new MySql.Data.MySqlClient.MySqlConnection(connectionString)) { Console.WriteLine("Opening"); await sqConnection.OpenAsync(); Console.WriteLine("Opened. Now Closing"); } } static async Task Example2() { //Lets pretend this is a database that my computer will try to connect to Console.WriteLine("Opening"); await Task.Delay(1000); //Lets say it takes 1 second to open Console.WriteLine("Opened. Now Closing"); } static void Main(string[] args) { Console.ReadLine(); Task.Run(() => Example()); Task.Run(() => Example()); Task.Run(() => Example()); Task.Run(() => Example()); Task.Run(() => Example()); Task.Run(() => Console.WriteLine("Done")); Console.ReadLine(); } 

这里当我运行Example()5次时,它将输出如下:

在此处输入图像描述

在写出“完成”之前需要3秒钟。 请注意,我的计算机根本不在CPU上工作,因为它只是等待并连接到数据库,大约需要1秒才能连接到。

所以它实际上阻止了我的计算机线程并且没有运行multithreading,否则它会立即写出“完成”。

所以,如果我调用Example2()而不是Example(),那么我得到的结果就是我想要的和我期望的结果:

在此处输入图像描述

在这里,它是真正的异步方法,因为我可以在我的计算机上一次完成6件事,只有2个核心。 但是第一个例子,我一次只能做两件事,因为MySQL异步方法不起作用。

我还使用sqConnection.Open()测试了它,它与sqConnection.OpenAsync()具有完全相同的结果

所以现在,我只是想弄清楚如何同时连接数据库5次。

从一些旧代码(6.7.2)来看,似乎mysql ADO.NET提供程序没有正确实现任何异步function。 这包括TAP模式和旧样式Begin …,End …异步模式。 在该版本中,Db *异步方法似乎根本没有写入; 他们将使用.NET中的基类,它们是同步的,看起来像:

 public virtual Task ExecuteNonQueryAsync(...) { return Task.FromResult(ExecuteNonQuery(...)); } 

(100%与在任务中包装它的额外开销同步; 参考源在这里 )

如果正确编写了Begin和End版本(它们不是),它可以实现如下:

 public override Task ExecuteNonQueryAsync(...) { return Task.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null); } 

( SqlCommand的该方法的参考源 )

这样做取决于底层套接字的某种回调api最终以一种模式处理,其中调用者通过套接字发送一些字节,然后注册的方法在准备就绪时从底层网络堆栈回调。

但是,mysql连接器不会这样做(它首先不会覆盖该方法;但如果确实如此,相关的开始和结束方法在某些底层套接字api上不是异步的)。 mysql连接器的作用是构建当前连接实例上的内部方法的委托,并在单独的线程上同步调用它。 你不能同时在同一个连接上执行第二个命令,例如:

 private static void Main() { var sw = new Stopwatch(); sw.Start(); Task.WaitAll( GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync(), GetDelayCommand().ExecuteNonQueryAsync()); sw.Stop(); Console.WriteLine(sw.Elapsed.Seconds); } private static DbCommand GetDelayCommand() { var connection = new MySqlConnection (...); connection.Open(); var cmd = connection.CreateCommand(); cmd.CommandText = "SLEEP(5)"; cmd.CommandType = CommandType.Text; return cmd; } 

(假设您是连接池并且任务数量超过最大池大小;如果异步工作,则此代码将获得一个数字,具体取决于池中的连接数而不是数字,具体取决于它和数量可以并发运行的线程)

这是因为代码锁定了驱动程序 (管理网络内部的实际内容; *)。 如果它没有(并且内部以其他方式线程安全并且其他方式用于管理连接池), 它继续在底层网络流上执行阻塞调用 。

所以是的,这个代码库没有异步支持。 如果有人能指出我的代码,我可以看一个更新的驱动程序,但我怀疑基于内部NetworkStream的对象看起来并没有显着不同,异步代码也没有太大的不同。 async支持驱动程序将编写大多数内部函数依赖于异步方式,并具有同步代码的同步包装器; 或者它看起来更像是SqlClient引用源,并依赖于一些Task包装库来抽象出同步或异步运行之间的差异。

*锁定驱动程序并不意味着它不可能使用非阻塞IO,只是该方法无法使用lock语句编写并使用可能已写入的非阻塞Begin / End IAsyncResult代码在TAP模式之前。

编辑:下载6.9.8; 怀疑没有正常运行的异步代码(非阻塞IO操作); 这里有一个错误: https : //bugs.mysql.com/bug.php?id = 70111

2016年7月6日更新:GitHub上有趣的项目,最终可能会在https://github.com/mysql-net/MySqlConnector上解决这个问题(可能会使用更多与其成功有关的贡献者[我不再处理任何与MySQL的])。

好吧,可能是有一个已经打开的汇集连接。 在这种情况下,它将同步返回给您。

需要注意的是await并不一定会将控制权返回给调用方法。 如果正在等待的任务的状态是TaskStatus.Running ,它只返回到调用方法。 如果任务已完成,则执行正常进行。

尝试等待此方法,该方法返回状态为RanToCompletion的任务:

 public Task SampleMethod() { return Task.FromResult(0); } 

避免在异步方法中使用void作为返回类型。 Async void仅适用于事件处理程序。 所有其他异步方法应返回TaskTask 。 试试这个:

 static async Task Example() { string connectionString = "Server=mydomainname.com;" + "Port=3306;" + "Database=scratch;" + "Uid=Assassinbeast;" + "Password=mypass123;" + "AllowUserVariables= true;"; MySql.Data.MySqlClient.MySqlConnection sqConnection = new MySql.Data.MySqlClient.MySqlConnection(connectionString); await sqConnection.OpenAsync(); Console.Write("Opened. Now Click to close"); Console.ReadLine(); sqConnection.Close(); } static void Main(string[] args) { Console.ReadLine(); Task.Run(() => Example()).Wait(); Console.WriteLine("Done"); Console.ReadLine(); } 

UPDATE

正如dcastro所解释的,这就是async-await的工作原理:

如果等待的任务尚未完成且仍在运行,则Example()将返回其调用方法,因此主线程不会被阻止。 当任务完成后,来自ThreadPool的线程(可以是任何线程)将返回到之前状态的Example()并继续执行。

或者第二种情况是任务已经完成执行并且结果可用。 当到达等待的任务时,编译器知道它具有结果并将继续在同一个线程上执行代码。