Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Os métodos assíncronos do MySQL C # não funcionam?


A julgar por algum código antigo (6.7.2), parece que o provedor mysql ADO.NET não implementa nenhuma funcionalidade assíncrona corretamente. Isso inclui o padrão TAP e os padrões assíncronos de estilo antigo Begin..., End.... Nessa versão, os métodos assíncronos Db* parecem não ter sido escritos; eles estariam usando os da classe base no .NET que são síncronos e todos se parecem com:
public virtual Task<int> ExecuteNonQueryAsync(...) {
    return Task.FromResult(ExecuteNonQuery(...));
}

(100% síncrono com a sobrecarga adicional de envolvê-lo em uma tarefa; fonte de referência aqui )

Se as versões Begin e End foram escritas corretamente (não são), poderia ser implementado algo assim:
public override Task<int> ExecuteNonQueryAsync(...) {
    return Task<int>.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null);
}

(fonte de referência para esse método para SqlCommand )

Fazer isso depende de algum tipo de API de retorno de chamada para o soquete subjacente para eventualmente lidar com um padrão em que o chamador envia alguns bytes pelo soquete e, em seguida, um método registrado é chamado de volta da pilha de rede subjacente quando estiver pronto.

No entanto, o conector mysql não faz isso (ele não substitui esse método em primeiro lugar; mas se o fez, os métodos de início e fim relevantes não são assíncronos em alguma API de soquete subjacente). O que o conector mysql faz em vez disso é construir um delegado para um método interno na instância de conexão atual e o invoca de forma síncrona em um thread separado. Enquanto isso, você não pode, por exemplo, executar um segundo comando na mesma conexão, algo assim:
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;
}

(assumindo que você está fazendo um pool de conexões e o número de tarefas é maior que o tamanho máximo do pool; se o async funcionasse, esse código obteria um número dependendo do número de conexões no pool em vez de um número dependendo tanto disso quanto do número de threads que podem ser executados simultaneamente)

Isso ocorre porque o código tem um travar o motorista (a coisa real que gerencia os internos da rede; *). E se isso não acontecer (e os internos eram seguros para threads e alguma outra maneira foi usada para gerenciar pools de conexão) ele continua realizando chamadas de bloqueio no fluxo de rede subjacente .

Então, sim, nenhum suporte assíncrono à vista para esta base de código. Eu poderia olhar para um driver mais recente se alguém pudesse me apontar para o código, mas suspeito que o NetworkStream interno objetos baseados não parecem significativamente diferentes e o código assíncrono também não parece muito diferente. Um async o driver de suporte teria a maioria dos componentes internos escritos para depender de uma maneira assíncrona de fazê-lo e teria um wrapper síncrono para o código síncrono; alternativamente, seria muito mais parecido com o SqlClient fonte de referência e depende de alguma Task biblioteca de encapsulamento para abstrair as diferenças entre a execução síncrona ou assíncrona.

* travar no driver não significa que ele não poderia estar usando E/S sem bloqueio, apenas que o método não poderia ter sido escrito com uma instrução de bloqueio e usar o Begin/End sem bloqueio IAsyncResult código que poderia ter sido escrito antes dos padrões TAP.

Editar:baixado 6.9.8; como suspeito, não há código assíncrono em funcionamento (operações de E/S sem bloqueio); há um bug registrado aqui:https://bugs.mysql.com/bug. php?id=70111

Atualização de 6 de julho de 2016:projeto interessante no GitHub que pode finalmente resolver isso em https://github.com/ mysql-net/MySqlConnector (provavelmente poderia usar mais contribuidores que tenham interesse em seu sucesso [não estou mais trabalhando em nada com o MySql]).