Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Asynchrone MySQL C#-Methoden funktionieren nicht?

Nach einigem alten Code (6.7.2) zu urteilen, scheint der mysql ADO.NET-Anbieter keine der asynchronen Funktionen korrekt zu implementieren. Dazu gehören das TAP-Muster und die asynchronen Muster Begin..., End... im älteren Stil. In dieser Version scheinen die asynchronen Db*-Methoden überhaupt nicht geschrieben zu sein; Sie würden die Basisklassen in .NET verwenden, die synchron sind und alle in etwa so aussehen:

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

(100 % synchron mit dem zusätzlichen Aufwand, es in eine Aufgabe zu packen; Referenzquelle hier )

Wenn die Begin- und End-Versionen korrekt geschrieben wären (sie sind es nicht), könnte es etwa so implementiert werden:

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

(Referenzquelle für diese Methode für SqlCommand )

Dies hängt von einer Art Rückruf-API ab, mit der der zugrunde liegende Socket schließlich in einem Muster umgehen kann, bei dem der Aufrufer einige Bytes über den Socket sendet und dann eine registrierte Methode vom zugrunde liegenden Netzwerkstapel zurückgerufen wird, wenn sie bereit ist.

Der mysql-Connector tut dies jedoch nicht (er überschreibt diese Methode nicht von vornherein; aber wenn dies der Fall wäre, sind die relevanten begin- und end-Methoden auf einigen zugrunde liegenden Socket-APIs nicht asynchron). Was der MySQL-Connector macht stattdessen erstellt einen Delegaten für eine interne Methode auf der aktuellen Verbindungsinstanz und ruft sie synchron in einem separaten Thread auf. Sie können in der Zwischenzeit beispielsweise keinen zweiten Befehl auf derselben Verbindung ausführen, etwa so:

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

(vorausgesetzt, Sie sind Verbindungspooling und die Anzahl der Aufgaben ist größer als die maximale Poolgröße; wenn Async funktionierte, würde dieser Code eine Zahl erhalten, die von der Anzahl der Verbindungen im Pool abhängt, anstatt eine Zahl, die sowohl davon als auch von der Anzahl abhängt Threads, die gleichzeitig ausgeführt werden können)

Dies liegt daran, dass der Code einen Sperren Sie den Treiber (das eigentliche Ding, das die Netzwerkinterna verwaltet; *). Und wenn dies nicht der Fall war (und die Interna ansonsten Thread-sicher waren und Verbindungspools auf andere Weise verwaltet wurden), Es fährt fort, blockierende Aufrufe für den zugrunde liegenden Netzwerkstream auszuführen .

Also ja, für diese Codebasis ist keine asynchrone Unterstützung in Sicht. Ich könnte mir einen neueren Treiber ansehen, wenn mich jemand auf den Code hinweisen könnte, aber ich vermute den internen NetworkStream basierte Objekte sehen nicht wesentlich anders aus und der asynchrone Code sieht auch nicht viel anders aus. Ein async Der unterstützende Treiber würde die meisten Interna so geschrieben haben, dass sie von einer asynchronen Methode abhängen, und einen synchronen Wrapper für den synchronen Code haben. alternativ würde es viel mehr wie der SqlClient aussehen Referenzquelle und hängen von irgendeiner Aufgabe ab Wrapping-Bibliothek, um die Unterschiede zwischen synchroner oder asynchroner Ausführung zu abstrahieren.

* Das Sperren des Treibers bedeutet nicht, dass er möglicherweise nicht blockierende IO verwenden könnte, nur dass die Methode nicht mit einer lock-Anweisung geschrieben werden konnte und den nicht blockierenden Begin/End IAsyncResult Code, der vor TAP-Mustern hätte geschrieben werden können.

Bearbeiten:heruntergeladen 6.9.8; wie vermutet gibt es keinen funktionierenden asynchronen Code (nicht blockierende IO-Operationen); Hier ist ein Fehler hinterlegt:https://bugs.mysql.com/bug. php?id=70111

Update 6. Juli 2016:interessantes Projekt auf GitHub, das sich vielleicht endlich damit befasst unter https://github.com/ mysql-net/MySqlConnector (könnte wahrscheinlich mehr Mitwirkende gebrauchen, die an seinem Erfolg beteiligt sind [ich arbeite an nichts mehr mit MySql]).