sql >> Databasteknik >  >> RDS >> Mysql

MySQL C# asynkmetoder fungerar inte?

Att döma av gammal kod (6.7.2) verkar det som att mysql ADO.NET-leverantören inte implementerar någon av asynkronfunktionerna korrekt. Detta inkluderar TAP-mönstret och de äldre stilarna Start..., End... asynkrona mönster. I den versionen verkar Db* async-metoderna inte vara skrivna alls; de skulle använda basklassen i .NET som är synkrona och alla ser ut ungefär som:

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

(100 % synkront med den extra omkostnaden för att lägga in den i en uppgift; referenskälla här )

Om start- och slutversionerna skrevs korrekt (de är inte det) skulle det kunna implementeras ungefär så här:

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

(referenskälla för den metoden för SqlCommand )

Att göra detta är beroende av någon form av callback-api för den underliggande socket att så småningom hantera i ett mönster där den som ringer skickar några byte över socket och sedan en registrerad metod anropas tillbaka från den underliggande nätverksstacken när den är klar.

Mysql-anslutningen gör dock inte detta (den åsidosätter inte den metoden i första hand, men om den gjorde det, är de relevanta start- och slutmetoderna inte asynkrona på någon underliggande socket-api). Vad mysql-anslutaren gör istället bygger en delegat till en intern metod på den aktuella anslutningsinstansen och anropar den synkront på en separat tråd. Du kan inte under tiden till exempel köra ett andra kommando på samma anslutning, ungefär så här:

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

(förutsatt att du anslutningspooler och antalet uppgifter är fler än den maximala poolstorleken; om asynkron fungerade skulle den här koden få ett nummer beroende på antalet anslutningar i poolen istället för ett antal beroende på både det och antalet trådar som kan köras samtidigt)

Detta beror på att koden har en lås på föraren (det faktiska som hanterar nätverkets interna delar; *). Och om det inte gjorde det (och internerna var annars trådsäkra och på något annat sätt användes för att hantera anslutningspooler) det fortsätter att utföra blockerande samtal på den underliggande nätverksströmmen .

Så ja, inget asynkstöd i sikte för denna kodbas. Jag skulle kunna titta på en nyare drivrutin om någon kunde peka mig till koden, men jag misstänker att den interna NetworkStream baserade objekt ser inte nämnvärt annorlunda ut och asynkronkoden ser inte heller mycket annorlunda ut. En async stödjande drivrutin skulle ha det mesta av internerna skrivna för att bero på ett asynkront sätt att göra det och ha ett synkront omslag för den synkrona koden; alternativt skulle det se mycket mer ut som SqlClient referenskälla och beror på någon Task wrapping-bibliotek för att abstrahera bort skillnaderna mellan att köra synkront eller asynkront.

* att låsa drivrutinen betyder inte att det omöjligen kan vara att använda icke-blockerande IO, bara att metoden inte kunde ha skrivits med en låssats och använd den icke-blockerande Begin/End IAsyncResult kod som kunde ha skrivits före TAP-mönster.

Edit:laddade ner 6.9.8; som misstänkt finns det ingen fungerande asynkronkod (icke-blockerande IO-operationer); det finns en bugg arkiverad här:https://bugs.mysql.com/bug. php?id=70111

Uppdatering 6 juli 2016:intressant projekt på GitHub som äntligen kan ta itu med detta på https://github.com/ mysql-net/MySqlConnector (kan förmodligen använda fler bidragsgivare som har en del i dess framgång [jag arbetar inte längre med något med MySql]).



  1. Användarregistrering och e-postverifiering PHP och MySQL

  2. JSON_UNQUOTE() – Ta bort citat från ett JSON-dokument i MySQL

  3. Hur man ansluter SalesForce som en datakälla i Pyramid

  4. MySql summaelement i en kolumn