简体   繁体   English

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

[英]MySQL C# async methods doesn't work?

i have a database in a server and it seems like the async method doesn't work. 我在服务器中有一个数据库,似乎async方法不起作用。

Here is my code: 这是我的代码:

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();
}

At the "await" statement, its actually supposed to jump back to the Main() function and writeout "Done". 在“await”语句中,它实际上应该跳回Main()函数并写出“Done”。 But its not doing that. 但它不这样做。 Its just running synchronously like it wasn't an async method and it will first write "Done" once the function is fully completed. 它只是同步运行,就像它不是异步方法一样,一旦功能完全完成,它将首先写入“完成”。

So what am i doing wrong? 那我做错了什么? Is it a bug? 这是一个错误吗?

UPDATE UPDATE

Okay, so after i got some answers, i actually still couldn't see any difference between the OpenAsync() and Open(). 好的,所以在我得到一些答案之后,我实际上仍然看不到OpenAsync()和Open()之间的任何区别。

I began trying to test more things out and i think i can conclude that the async method DOES NOT WORK 我开始尝试测试更多的东西,我想我可以得出结论,异步方法不起作用

Here is my new code: 这是我的新代码:

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();
}

Here when i run the Example() 5 times, it will output like this: 这里当我运行Example()5次时,它将输出如下:

在此输入图像描述

It takes up to 3 seconds before it will write out "Done". 它需要3秒才会写出“完成”。 Note that my computer is not working on the CPU at all, because it is only waiting and connecting to the database which takes around 1 second to connect to. 请注意,我的计算机根本不在CPU上工作,因为它只等待并连接到数据库,大约需要1秒才能连接到。

So its actually blocking my computers thread and not running multithreaded, otherwise it would write out "Done" immediedtly. 所以它实际上阻止了我的计算机线程并且没有运行多线程,否则它会立即写出“完成”。

So if i called Example2() instead of Example(), then i get this result which is what i want and what i would expect: 所以如果我调用Example2()而不是Example(),那么我得到的结果就是我想要的和我期望的结果:

在此输入图像描述

Here, it is true async method, because i can do over 6 things at a time on my computer which has only 2 cores. 在这里,它是真正的异步方法,因为我可以在我的计算机上一次完成6件事,只有2个核心。 But the first example, i could only do two things at a time because MySQL async method does not work. 但是第一个例子,我一次只能做两件事,因为MySQL异步方法不起作用。

I also tested it with sqConnection.Open(), which had the exact same result as sqConnection.OpenAsync() 我还使用sqConnection.Open()测试了它,它与sqConnection.OpenAsync()具有完全相同的结果

So right now, i just cant figure out how to connect to the database 5 times at the same time. 所以现在,我只是想弄清楚如何同时连接数据库5次。

Judging from some old code (6.7.2), it appears that the mysql ADO.NET provider does not implement any of the async functionality correctly. 从一些旧代码(6.7.2)来看,似乎mysql ADO.NET提供程序没有正确实现任何异步功能。 This includes the TAP pattern and the older style Begin..., End... async patterns. 这包括TAP模式和旧样式Begin ...,End ...异步模式。 In that version, the Db* async methods appear to not be written at all; 在该版本中,Db *异步方法似乎根本没有写入; they would be using the base class ones in .NET which are synchronous and all look something like: 他们将使用.NET中的基类类,它们是同步的,看起来像:

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

(100% synchronous with the added overhead of wrapping it in a task; reference source here ) (与在任务中包装它的额外开销100%同步; 参考源在这里

If the Begin and End versions were written correctly (they aren't) it could be implemented something like this: 如果Begin和End版本写得正确(它们不是),它可以实现如下:

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

( reference source for that method for SqlCommand ) SqlCommand的该方法的参考源

Doing this is dependent on some sort of callback api for the underlying socket to eventually deal with in a pattern where the caller sends some bytes over the socket and then a registered method gets called back from the underlying network stack when it is ready. 这样做取决于底层套接字的某种回调api最终处理的模式,其中调用者通过套接字发送一些字节,然后注册的方法在准备就绪时从底层网络堆栈回调。

However, the mysql connector doesn't do this (it doesn't override that method in the first place; but if it did, the relevant begin and end methods aren't async on some underlying socket api). 但是,mysql连接器不会这样做(它首先不会覆盖该方法;但如果确实如此,相关的开始和结束方法在某些底层套接字api上不是异步的)。 What the mysql connector does instead is build a delegate to an internal method on the current connection instance and invokes it synchronously on a separate thread. 什么MySQL的连接器做代替是建立一个代表对当前的连接实例的内部的方法和同步调用它在一个单独的线程。 You cannot in the meantime for example execute a second command on the same connection, something like this: 你不能同时在同一个连接上执行第二个命令,例如:

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;
}

(assuming you are connection pooling and the number of tasks there is more than the maximum pool size; if async worked this code would get a number depending on the number of connections in the pool instead of a number depending on both that and the number of threads that can run concurrently) (假设您是连接池,并且任务数量超过最大池大小;如果异步工作,则此代码将获得一个数字,具体取决于池中的连接数而不是数字,具体取决于该数量和可以并发运行的线程)

This is because the code has a lock on the driver (the actual thing that manages the network internals; *). 这是因为代码锁定了驱动程序 (管理网络内部的实际内容; *)。 And if it didn't (and the internals were otherwise thread safe and some other way was used to manage connection pools) it goes on to perform blocking calls on the underlying network stream . 如果它没有(并且内部是其他线程安全的,而其他方式用于管理连接池), 它继续在底层网络流上执行阻塞调用

So yeah, no async support in sight for this codebase. 所以是的,这个代码库没有异步支持。 I could look at a newer driver if someone could point me to the code, but I suspect the internal NetworkStream based objects do not look significantly different and the async code is not looking much different either. 如果有人能指出我的代码,我可以看一个更新的驱动程序,但我怀疑基于内部NetworkStream的对象看起来并没有显着不同,异步代码也没有太大的不同。 An async supporting driver would have most of the internals written to depend on an asynchronous way of doing it and have a synchronous wrapper for the synchronous code; async支持驱动程序将编写大多数内部函数依赖于异步方式,并具有同步代码的同步包装器; alternatively it would look a lot more like the SqlClient reference source and depend on some Task wrapping library to abstract away the differences between running synchronously or async. 或者它看起来更像是SqlClient引用源,并依赖于一些Task包装库来抽象出同步或异步运行之间的差异。

* locking on driver doesn't mean it couldn't possibly be using non-blocking IO, just that the method couldn't have been written with a lock statement and use the non-blocking Begin/End IAsyncResult code that could have been written prior to TAP patterns. *锁定驱动程序并不意味着它不可能使用非阻塞IO,只是该方法无法使用lock语句编写并使用可能已写入的非阻塞Begin / End IAsyncResult代码在TAP模式之前。

Edit: downloaded 6.9.8; 编辑:下载6.9.8; as suspected there is no functioning async code (non-blocking IO operations); 怀疑没有正常的异步代码(非阻塞IO操作); there is a bug filed here: https://bugs.mysql.com/bug.php?id=70111 这里有一个错误: https//bugs.mysql.com/bug.php?id = 70111

Update July 6 2016: interesting project on GitHub which may finally address this at https://github.com/mysql-net/MySqlConnector (could probably use more contributors that have a stake in its success [I am no longer working on anything with MySql]). 2016年7月6日更新:关于GitHub的有趣项目,最终可能会在https://github.com/mysql-net/MySqlConnector上解决这个问题(可能会使用更多与其成功有关的贡献者[我不再处理任何与MySQL的])。

Well, it could be that there's a pooled connection that's already open. 好吧,可能是有一个已经打开的池连接。 In which case, it would be returned to you synchronously. 在这种情况下,它将同步返回给您。

The thing to notice is that await does not necessarily return control back to the calling method. 需要注意的是await并不一定会将控制权返回给调用方法。 It only returns to the calling method if the status of the task being awaited is TaskStatus.Running . 如果正在等待的任务的状态是TaskStatus.Running ,它只返回到调用方法。 If the task has completed, then execution carries on as normal. 如果任务已完成,则执行正常进行。

Try awaiting this method, which returns a task with status RanToCompletion : 尝试等待此方法,该方法返回状态为RanToCompletion的任务:

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

Avoid using void as return type in async methods. 避免在异步方法中使用void作为返回类型。 Async void is just meant for event handlers. Async void仅适用于事件处理程序。 All other async methods should return Task or Task<T> . 所有其他异步方法应返回TaskTask<T> Try this: 尝试这个:

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 UPDATE

As explained by dcastro, this is how async-await works: 正如dcastro所解释的,这就是async-await的工作原理:

If the awaited task hasn't finished and is still running, Example() will return to its calling method, thus the main thread doesn't get blocked. 如果等待的任务尚未完成且仍在运行,则Example()将返回其调用方法,因此主线程不会被阻止。 When the task is done then a thread from the ThreadPool (can be any thread) will return to Example() at its previous state and continue execution. 当任务完成后,来自ThreadPool的线程(可以是任何线程)将返回到之前状态的Example()并继续执行。

Or A second case would be that the task has already finished its execution and the result is available. 或者第二种情况是任务已经完成执行并且结果可用。 When reaching the awaited task the compiler knows that it has the result and will keep on executing code on the very same thread. 当到达等待的任务时,编译器知道它具有结果并将继续在同一个线程上执行代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM