sql >> Base de Datos >  >> RDS >> Mysql

¿Los métodos asíncronos de MySQL C # no funcionan?

A juzgar por un código antiguo (6.7.2), parece que el proveedor mysql ADO.NET no implementa correctamente ninguna de las funciones asíncronas. Esto incluye el patrón TAP y los patrones asincrónicos Begin..., End... de estilo anterior. En esa versión, los métodos asíncronos Db* parecen no estar escritos en absoluto; estarían usando los de la clase base en .NET que son sincrónicos y todos se parecen a:

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

(100 % síncrono con la sobrecarga adicional de incluirlo en una tarea; fuente de referencia aquí )

Si las versiones Begin y End se escribieron correctamente (no lo están), se podría implementar algo como esto:

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

(fuente de referencia para ese método para SqlCommand )

Hacer esto depende de algún tipo de API de devolución de llamada para que el socket subyacente finalmente se ocupe de un patrón en el que la persona que llama envía algunos bytes a través del socket y luego se vuelve a llamar a un método registrado desde la pila de red subyacente cuando está listo.

Sin embargo, el conector mysql no hace esto (no anula ese método en primer lugar; pero si lo hiciera, los métodos de inicio y fin relevantes no son asíncronos en alguna API de socket subyacente). Lo que hace el conector mysql en cambio es construir un delegado a un método interno en la instancia de conexión actual y lo invoca sincrónicamente en un subproceso separado. Mientras tanto, por ejemplo, no puede ejecutar un segundo comando en la misma conexión, algo como esto:

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

(suponiendo que está agrupando conexiones y que la cantidad de tareas es mayor que el tamaño máximo del grupo; si asíncrono funcionara, este código obtendría un número dependiendo de la cantidad de conexiones en el grupo en lugar de un número que depende tanto de eso como de la cantidad de subprocesos que pueden ejecutarse simultáneamente)

Esto se debe a que el código tiene un bloquear el controlador (lo real que gestiona los internos de la red; *). Y si no fuera así (y las partes internas eran seguras para subprocesos y se usó alguna otra forma para administrar los grupos de conexiones) pasa a realizar llamadas de bloqueo en el flujo de red subyacente .

Así que sí, no hay soporte asincrónico a la vista para este código base. Podría buscar un controlador más nuevo si alguien pudiera señalarme el código, pero sospecho que el NetworkStream interno los objetos basados ​​no se ven significativamente diferentes y el código asíncrono tampoco se ve muy diferente. Un asincrónico el controlador de soporte tendría la mayoría de las partes internas escritas para depender de una forma asíncrona de hacerlo y tendría un envoltorio síncrono para el código síncrono; alternativamente, se parecería mucho más al SqlClient fuente de referencia y dependen de alguna Tarea biblioteca de envoltura para abstraer las diferencias entre la ejecución sincrónica o asincrónica.

* bloquear el controlador no significa que no pueda estar usando E/S sin bloqueo, solo que el método no se pudo haber escrito con una declaración de bloqueo y usar Begin/End IAsyncResult código que podría haber sido escrito antes de los patrones TAP.

Editar:descargado 6.9.8; como se sospecha, no hay un código asíncrono que funcione (operaciones de E/S sin bloqueo); hay un error presentado aquí:https://bugs.mysql.com/bug. php?id=70111

Actualización del 6 de julio de 2016:proyecto interesante en GitHub que finalmente puede abordar esto en https://github.com/ mysql-net/MySqlConnector (Probablemente podría usar más colaboradores que tengan interés en su éxito [ya no estoy trabajando en nada con MySql]).