Combinazione efficiente di metodi di sincronizzazione e asincroni in un unico metodo?

10

Ok, sembra strano, ma il codice è molto semplice e spiega bene la situazione.

public virtual async Task RemoveFromRoleAsync(AzureTableUser user, string role)
{
    AssertNotDisposed();
    var roles = await GetRolesForUser(user);
    roles.Roles = RemoveRoles(roles.Roles, role);
    await Run(TableOperation.Replace(roles));
}

(So che sto parlando in astratto qui sotto, ma quanto sopra è un metodo effettivo da quello che sarà il vero codice di produzione che sta effettivamente facendo quello che sto chiedendo qui, e sono realmente interessato a lo stai effettivamente rivedendo per correttezza rispetto al modello asincrono / atteso.)

Sto incontrando questo schema sempre più spesso ora che sto usando async / await di più. Il pattern è costituito dalla seguente catena di eventi:

  1. Attendi una chiamata iniziale che mi fornisca alcune informazioni su cui devo lavorare
  2. Lavora in modo sincrono su tali informazioni
  3. Attendi una chiamata finale che salva il lavoro aggiornato

Il blocco di codice sopra riportato è in genere il modo in cui utilizzo questi metodi. I await la prima chiamata, che devo perché è asincrona. Successivamente, eseguo il lavoro che devo eseguire, che non è legato all'IO o alla risorsa, quindi non è asincrono. Infine, salverò il mio lavoro che è anche una chiamata async , e fuori carico-cult I await it.

Ma è questo il modo più efficiente / corretto per gestire questo modello? Mi sembra che potrei saltare await nell'ultima chiamata, ma cosa succede se fallisce? E dovrei usare un metodo Task come ContinueWith per concatenare il mio lavoro sincrono con la chiamata originale? Sono solo in un punto in questo momento in cui non sono sicuro se sto gestendo correttamente questo.

Dato il codice nell'esempio , c'è un modo migliore per gestire questa catena di chiamate del metodo asincrono / sincronizzazione / asincrona?

    
posta Will 26.01.2014 - 20:04
fonte

2 risposte

3

Sì, penso che questo sia il modo giusto per farlo.

Non puoi saltare il secondo await . Se lo facessi, il metodo sembrerebbe completare troppo presto (prima che la rimozione venisse effettivamente eseguita) e non scopriresti mai se la rimozione non è andata a buon fine.

Non vedo come sarebbe ContinueWith() o qualcosa del genere aiutare qui. Potresti usarlo per evitare di usare await , ma renderebbe il tuo codice più complicato e meno leggibile. E questo è l'intero punto di await : rendere più semplice la scrittura di codice asincrono rispetto all'utilizzo di continuazioni.

    
risposta data 08.02.2014 - 18:16
fonte
0

Il modo di gestire questo modello è quello di garantire che tutto l'I / O sia asincrono. I metodi I / O sincroni causano il blocco del thread corrente mentre attende una risposta dalla destinazione I / O (rete, file system, ecc.).

Un'altra cosa da considerare è che await dovrebbe essere usato quando hai bisogno di un valore di ritorno o quando hai bisogno del codice atteso per finire prima di fare qualcos'altro. Se non hai bisogno di nessuna di queste cose, puoi "attivare e disattivare" il tuo metodo asincrono con Task.Run .

Quindi, per l'uso più efficiente delle risorse di calcolo, se RemoveRoles fa un I / O, dovrebbe diventare await RemoveRolesAsync ei metodi I / O chiamati da RemoveRolesAsync dovrebbero anche essere asincroni (e probabilmente attesi) .

Se le prestazioni non sono la tua massima preoccupazione, allora va bene fare un po 'di I / O sincrono su un thread asincrono. È un debito tecnico però. (In questo caso, potresti voler chiamare il primo metodo asincrono con ConfigureAwait , a seconda di dove il codice è in esecuzione.)

Ecco un approfondimento delle best practice: link

Ecco alcune note sul comportamento di ConfigureAwait in ambienti diversi come ASP.NET, WebAPI, ecc. - link

    
risposta data 06.03.2015 - 02:52
fonte

Leggi altre domande sui tag