Miscelazione efficiente di metodi di sincronizzazione e asincrono all'interno di un singolo metodo?


11

Va bene, 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 qualche modo nell'abstract qui sotto, ma quanto sopra è un metodo reale da quello che sarà il vero codice di produzione che sta effettivamente facendo quello che sto chiedendo qui, e sono davvero interessato alla tua recensione per correttezza nei confronti del modello asincrono / attesa.)

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

  1. Attendere una chiamata iniziale che mi dia alcune informazioni su cui ho bisogno di lavorare
  2. Lavora su tali informazioni in modo sincrono
  3. Attendere un'ultima chiamata per salvare il lavoro aggiornato

Il blocco di codice sopra è in genere il modo in cui gestisco questi metodi. Io awaitla prima chiamata, che devo fare perché è asincrona. Successivamente, faccio il lavoro che devo fare che non è associato a IO o alle risorse, quindi non è asincrono. Infine, salvo il mio lavoro, che è anche un asyncappello, e per culto await.

Ma è questo il modo più efficiente / corretto per gestire questo modello? Mi sembra che potrei saltare awaitl'ultima chiamata, ma cosa succede se fallisce? E dovrei usare un Taskmetodo come quello ContinueWithdi concatenare il mio lavoro sincrono con la chiamata originale? Sono solo in un momento in cui non sono sicuro di gestirlo correttamente.

Dato il codice nell'esempio , esiste un modo migliore per gestire questa catena di chiamate del metodo asincrono / sincronizzato / asincrono?


Il codice mi sembra il più breve e comprensibile possibile. Tutto ciò a cui riesco a pensare introduce complessità inutili.
Euforico,

@Euforico: dovrei preoccuparmi di aspettare la mia ultima chiamata? Cosa succederebbe se non lo facessi e lo gettassi? Sarebbe diverso come è attualmente?
Strappato il

Risposte:


3

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

Non puoi saltare il secondo await. In tal caso, il metodo sembrerebbe completarsi troppo presto (prima che la rimozione fosse effettivamente eseguita) e non scopriresti mai se la rimozione non è riuscita.

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


0

Il modo di gestire questo modello è garantire che tutti gli I / O siano asincroni. 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 awaitdovrebbe 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 "sparare e dimenticare" con il tuo metodo asincrono Task.Run.

Quindi, per un uso più efficiente delle risorse informatiche, in caso di RemoveRolesI / O, dovrebbe diventare await RemoveRolesAsynce anche i metodi di I / O chiamati RemoveRolesAsyncdovrebbero essere asincroni (e possibilmente attesi).

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

Ecco uno sguardo più approfondito alle migliori pratiche - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

Ecco alcune note sul comportamento di ConfigureAwait in diversi ambienti come ASP.NET, WebAPI, ecc. - /programming/13489065/best-practice-to-call-configureawait-for-all-server-side -codice


2
Non devi mai sparare e dimenticare il codice con Task.Run. Tutti i compiti dovrebbero essere attesi. Se non si attende l'attività e l'attività viene raccolta in modo inutile in uno stato in cui l'eccezione è "non osservata", il runtime genererà UnobservedTaskException e potrebbe arrestare in modo anomalo l'applicazione in base alla versione del framework.
Triynko,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.