sql >> Database >  >> RDS >> Mysql

MySQL C# asynchrone methoden werken niet?

Afgaande op een oude code (6.7.2), lijkt het erop dat de mysql ADO.NET-provider geen van de async-functionaliteit correct implementeert. Dit omvat het TAP-patroon en de oudere stijlen Begin..., Einde... asynchrone patronen. In die versie lijken de asynchrone methoden van Db* helemaal niet te zijn geschreven; ze zouden de basisklassen in .NET gebruiken die synchroon zijn en er allemaal ongeveer zo uitzien:

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

(100% synchroon met de toegevoegde overhead van het inpakken van een taak; referentiebron hier )

Als de Begin- en End-versies correct waren geschreven (dat zijn ze niet), zou het ongeveer als volgt kunnen worden geïmplementeerd:

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

(referentiebron voor die methode voor SqlCommand )

Dit doen is afhankelijk van een soort callback-API voor de onderliggende socket om uiteindelijk af te handelen in een patroon waarbij de beller enkele bytes over de socket verzendt en vervolgens wordt een geregistreerde methode teruggeroepen vanuit de onderliggende netwerkstack wanneer deze gereed is.

De mysql-connector doet dit echter niet (hij heft die methode in de eerste plaats niet op, maar als dat wel het geval is, zijn de relevante begin- en eindmethoden niet async op een onderliggende socket-api). Wat de mysql-connector doet in plaats daarvan bouwt een gedelegeerde naar een interne methode op de huidige verbindingsinstantie en roept deze synchroon aan op een aparte thread. U kunt in de tussentijd bijvoorbeeld geen tweede commando uitvoeren op dezelfde verbinding, zoiets als dit:

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

(ervan uitgaande dat u verbindingspooling bent en het aantal taken dat er is meer is dan de maximale poolgrootte; als asynchrone zou werken, zou deze code een nummer krijgen dat afhankelijk is van het aantal verbindingen in de pool in plaats van een nummer afhankelijk van zowel dat als het aantal threads die gelijktijdig kunnen worden uitgevoerd)

Dit komt omdat de code een slot op de bestuurder (het eigenlijke ding dat het interne netwerk beheert; *). En als dat niet het geval was (en de interne onderdelen waren verder thread-safe en er werd een andere manier gebruikt om verbindingspools te beheren) het gaat verder met het blokkeren van oproepen op de onderliggende netwerkstroom .

Dus ja, geen asynchrone ondersteuning in zicht voor deze codebase. Ik zou naar een nieuwere driver kunnen kijken als iemand me naar de code zou kunnen verwijzen, maar ik vermoed dat de interne NetworkStream gebaseerde objecten zien er niet significant anders uit en de asynchrone code ziet er ook niet veel anders uit. Een asynchrone ondersteunende driver zou hebben dat de meeste interne onderdelen afhankelijk zijn van een asynchrone manier om het te doen en een synchrone wrapper hebben voor de synchrone code; als alternatief zou het veel meer lijken op de SqlClient referentiebron en zijn afhankelijk van een Taak inpakbibliotheek om de verschillen tussen synchroon of asynchroon weg te werken.

* stuurprogramma vergrendelen betekent niet dat het onmogelijk een niet-blokkerende IO kan gebruiken, alleen dat de methode niet geschreven kan zijn met een lock-instructie en de niet-blokkerende Begin/End IAsyncResult code die vóór TAP-patronen geschreven had kunnen zijn.

Bewerken:6.9.8 gedownload; zoals vermoed is er geen werkende asynchrone code (niet-blokkerende IO-bewerkingen); er is hier een bug ingediend:https://bugs.mysql.com/bug. php?id=70111

Update 6 juli 2016:interessant project op GitHub dat dit eindelijk kan aanpakken op https://github.com/ mysql-net/MySqlConnector (kunnen waarschijnlijk meer bijdragers gebruiken die belang hebben bij het succes ervan [Ik werk nergens meer aan met MySql]).



  1. Hoe Coalesce() werkt in SQLite

  2. De ADO.NET-provider met de invariante naam 'MySql.Data.MySqlClient' is niet geregistreerd in het configuratiebestand van de machine of de toepassing

  3. Een globale tijdelijke tabel maken in Oracle

  4. Soms KUNT u een kolom op zijn plaats vergroten