Mysql
 sql >> Base de données >  >> RDS >> Mysql

Les méthodes asynchrones MySQL C # ne fonctionnent pas?

À en juger par un ancien code (6.7.2), il semble que le fournisseur mysql ADO.NET n'implémente correctement aucune des fonctionnalités asynchrones. Cela inclut le modèle TAP et les modèles asynchrones Begin..., End... de style plus ancien. Dans cette version, les méthodes asynchrones Db* semblent ne pas être écrites du tout ; ils utiliseraient ceux de la classe de base dans .NET qui sont synchrones et ressemblent tous à :

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

(100 % synchrone avec la surcharge supplémentaire de l'encapsuler dans une tâche ; source de référence ici )

Si les versions Begin et End ont été écrites correctement (elles ne le sont pas), elles pourraient être implémentées comme suit :

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

(source de référence pour cette méthode pour SqlCommand )

Cela dépend d'une sorte d'API de rappel que le socket sous-jacent doit éventuellement traiter dans un modèle où l'appelant envoie des octets sur le socket, puis une méthode enregistrée est rappelée à partir de la pile réseau sous-jacente lorsqu'elle est prête.

Cependant, le connecteur mysql ne le fait pas (il ne remplace pas cette méthode en premier lieu, mais si c'est le cas, les méthodes de début et de fin pertinentes ne sont pas asynchrones sur une API de socket sous-jacente). Ce que fait le connecteur mysql à la place est de construire un délégué à une méthode interne sur l'instance de connexion actuelle et de l'invoquer de manière synchrone sur un thread séparé. Vous ne pouvez pas entre-temps par exemple exécuter une deuxième commande sur la même connexion, quelque chose comme ceci :

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

(en supposant que vous regroupez des connexions et que le nombre de tâches dépasse la taille maximale du pool; si async fonctionnait, ce code obtiendrait un nombre en fonction du nombre de connexions dans le pool au lieu d'un nombre en fonction à la fois de cela et du nombre de threads pouvant s'exécuter simultanément)

C'est parce que le code a un verrouiller le pilote (la chose réelle qui gère les composants internes du réseau ; *). Et si ce n'était pas le cas (et que les composants internes étaient par ailleurs thread-safe et qu'un autre moyen était utilisé pour gérer les pools de connexions) il continue à effectuer des appels de blocage sur le flux réseau sous-jacent .

Alors oui, pas de support asynchrone en vue pour cette base de code. Je pourrais regarder un pilote plus récent si quelqu'un pouvait me diriger vers le code, mais je soupçonne le NetworkStream interne les objets basés ne semblent pas significativement différents et le code asynchrone n'est pas très différent non plus. Un async le pilote de support aurait la plupart des éléments internes écrits pour dépendre d'une manière asynchrone de le faire et aurait un wrapper synchrone pour le code synchrone ; sinon, il ressemblerait beaucoup plus au SqlClient source de référence et dépendent de certaines Task bibliothèque d'encapsulation pour faire abstraction des différences entre l'exécution synchrone ou asynchrone.

* le verrouillage sur le pilote ne signifie pas qu'il ne pourrait pas utiliser d'E/S non bloquantes, mais simplement que la méthode n'aurait pas pu être écrite avec une instruction de verrouillage et utiliser le Begin/End non bloquant IAsyncResult code qui aurait pu être écrit avant les modèles TAP.

Modifier :téléchargé 6.9.8 ; comme suspecté, il n'y a pas de code asynchrone fonctionnel (opérations d'E/S non bloquantes) ; il y a un bogue enregistré ici :https://bugs.mysql.com/bug. php?id=70111

Mise à jour du 6 juillet 2016 :projet intéressant sur GitHub qui pourrait enfin résoudre ce problème sur https://github.com/ mysql-net/MySqlConnector (pourrait probablement utiliser plus de contributeurs qui ont un intérêt dans son succès [je ne travaille plus sur rien avec MySql]).