Mysql
 sql >> Database >  >> RDS >> Mysql

I metodi asincroni di MySQL C# non funzionano?

A giudicare da un vecchio codice (6.7.2), sembra che il provider mysql ADO.NET non implementi correttamente nessuna delle funzionalità asincrone. Ciò include il modello TAP e lo stile precedente Begin..., End... modelli asincroni. In quella versione, i metodi asincroni Db* sembrano non essere scritti affatto; userebbero quelli della classe base in .NET che sono sincroni e assomigliano tutti a:

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

(100% sincrono con l'overhead aggiunto di avvolgerlo in un'attività; fonte di riferimento qui )

Se le versioni Begin e End sono state scritte correttamente (non lo sono) potrebbe essere implementato qualcosa del genere:

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

(fonte di riferimento per quel metodo per SqlCommand )

Ciò dipende da una sorta di API di callback che il socket sottostante può eventualmente gestire in uno schema in cui il chiamante invia alcuni byte sul socket e quindi un metodo registrato viene richiamato dallo stack di rete sottostante quando è pronto.

Tuttavia, il connettore mysql non lo fa (in primo luogo non sovrascrive quel metodo; ma in tal caso, i metodi di inizio e fine pertinenti non sono asincroni su alcune API socket sottostanti). Che cosa fa il connettore mysql invece compila un delegato a un metodo interno nell'istanza di connessione corrente e lo richiama in modo sincrono su un thread separato. Non puoi nel frattempo eseguire ad esempio un secondo comando sulla stessa connessione, qualcosa del genere:

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

(supponendo che tu stia effettuando un pool di connessioni e che il numero di attività sia superiore alla dimensione massima del pool; se asincrono funzionasse, questo codice otterrebbe un numero a seconda del numero di connessioni nel pool anziché un numero a seconda sia di quello che del numero di thread che possono essere eseguiti contemporaneamente)

Questo perché il codice ha un blocca il conducente (l'effettiva cosa che gestisce gli interni della rete; *). E se così non fosse (e gli interni erano altrimenti thread-safe e un altro modo è stato utilizzato per gestire i pool di connessioni) continua a eseguire chiamate di blocco sul flusso di rete sottostante .

Quindi sì, nessun supporto asincrono in vista per questa base di codice. Potrei guardare un driver più recente se qualcuno potesse indicarmi il codice, ma sospetto che il NetworkStream interno gli oggetti basati non hanno un aspetto significativamente diverso e anche il codice asincrono non sembra molto diverso. Un async il driver di supporto avrebbe la maggior parte degli interni scritti per dipendere da un modo asincrono di farlo e avrebbe un wrapper sincrono per il codice sincrono; in alternativa assomiglierebbe molto di più a SqlClient fonte di riferimento e dipendono da alcune Task libreria di wrapping per astrarre le differenze tra l'esecuzione sincrono o asincrona.

* il blocco sul driver non significa che non potrebbe utilizzare IO non bloccante, solo che il metodo non potrebbe essere stato scritto con un'istruzione di blocco e utilizzare il codice Begin/End non bloccante IAsyncResult codice che potrebbe essere stato scritto prima dei modelli TAP.

Modifica:scaricato 6.9.8; come sospetto non esiste un codice asincrono funzionante (operazioni IO non bloccanti); c'è un bug archiviato qui:https://bugs.mysql.com/bug. php?id=70111

Aggiornamento 6 luglio 2016:interessante progetto su GitHub che potrebbe finalmente affrontare questo problema su https://github.com/ mysql-net/MySqlConnector (probabilmente potrebbero utilizzare più contributori che hanno un interesse nel suo successo [non sto più lavorando su nulla con MySql]).