2014-11-17 17 views
8

Sto lavorando a un progetto di API Web che utilizza il servizio di cache gestita di Azure per memorizzare nella cache i risultati del database in memoria per migliorare i tempi di risposta e alleviare il traffico duplicato nel database. Quando si tenta di inserire un nuovo elemento nella cache, occasionalmente verrà generata un'eccezione specifica della cache con un codice di DataCacheErrorCode.RetryLater. Naturalmente, per riprovare più tardi senza dover bloccare questo metodo, l'ho reso async e await Task.Delay per riprovare a breve. Precedentemente uno sviluppatore aveva codificato in codice un Thread.Sleep in quello che stava davvero danneggiando le prestazioni delle applicazioni.Considerazioni per non attendere un'attività in un metodo asincrono

La firma di metodo sembra qualcosa di simile a questo ora:

public static async Task Put(string cacheKey, object obj) 

A seguito di questo cambiamento ho ~ 75 avvisi del compilatore da tutti gli altri posti nel applicazione che ha chiamato la versione precedentemente sincrona di Put indicando:

Poiché questa chiamata non è attesa, l'esecuzione del metodo corrente continua prima che la chiamata sia completata. Prendi in considerazione l'applicazione dell'operatore "Attendi" al risultato della chiamata.

In questo caso, dal momento che Put non restituisce nulla, ha senso per me di lasciare che questa operazione fire-and-forget come io non vedo alcun motivo per bloccare l'esecuzione del metodo che ha chiamato esso. Mi stavo solo chiedendo se ci sono pericoli o insidie ​​per aver permesso che molti di questi fire-and-forget Task s in esecuzione in background come Put possano essere chiamati abbastanza spesso. O dovrei comunque aspettare dal momento che il 99% delle volte non otterrò l'errore e il Task terminerà quasi immediatamente. Voglio solo assicurarmi di non incorrere in alcuna penalità per avere troppi thread (o qualcosa del genere).

risposta

6

Se c'è una possibilità Put getterà qualsiasi altra eccezione per qualsiasi tipo di ragione, e non si utilizza await Put ogni volta che si sta inserendo un oggetto nella cache, le eccezioni saranno inghiottiti all'interno della tornata Task che non è in attesa Se si utilizza .NET 4.0, questa eccezione verrà reinviata all'interno del Finalizer di tale Task.. Se stai usando .NET 4.5, sarà semplicemente ignorato (e potrebbe non essere desiderabile).

Desidero essere sicuro di non incorrere in alcuna penalità per avere troppo thread o qualcosa del genere.

Sto solo dicendo questo per chiarire le cose. Quando si utilizza Task.Delay, non si sta ruotando alcun nuovo thread. A Task non è sempre uguale a un nuovo thread che viene centrifugato. Nello specifico, Task.Delay utilizza internamente uno Timer, quindi non ci sono overhead dei thread (eccetto per il thread che è attualmente in ritardo se si utilizza await).

+0

Grazie per il buon feedback. Attualmente tutte le eccezioni diverse da RetryLater vengono catturate e registrate correttamente e siamo su .NET 4.5 quindi sono fiducioso nel lasciare che queste attività si attivino e si dimentichino. –

+0

Tecnicamente Timer utilizza un thread [separato] da un pool di thread, quindi Task.Delay() – alexm

+2

@alexm Utilizza un thread pool di thread per un periodo di tempo molto breve trascorso il tempo necessario per eseguire il gestore. Non consuma un thread thread thread durante l'attesa. Ciò significa che un thread viene sempre utilizzato solo quando ha un lavoro produttivo da fare, che è esattamente quello che vuoi (e non può evitare). – Servy

2

L'avviso ti sta dicendo che stai prendendo fuoco e dimentichi il comportamento in un luogo in cui potresti non voler realmente sparare e dimenticare. Se vuoi davvero licenziare e dimenticare e non avere problemi a continuare senza sapere mai quando l'operazione è finita, o se è stata completata correttamente, puoi tranquillamente ignorare l'avviso.

+0

+ 1; Per contrassegnare il metodo come esplicito "fire and forget", è possibile modificare il tipo di ritorno da async Task a async void. – alexm

+0

@alexm async void non si adatta alle API Web Ho ricevuto eccezioni in passato che indica che non è consentito quando ho provato ad usarlo –

+1

@alexm Ciò renderebbe l'unico modo in cui il metodo potrebbe mai essere chiamato è attraverso il fuoco e dimenticare, che è diverso da * permettendo * il metodo da attendere, ma semplicemente eseguire il fuoco e dimenticare la semantica in determinate situazioni per determinati chiamanti. – Servy

0

ASP consigliato.modo NET è

HostingEnvironment.QueueBackgroundWorkItem(WorkItem); 

... 

async Task WorkItem(CancellationToken cancellationToken) 
{ 
    try { await ...} catch (Exception e) { ... } 
} 

BTW Non cattura/ri-tiro sul filo altro allora filo ASP.NET può causare processo server da schiantato/riavviato

1

Un risultato negativo del rilascio di un compito da eseguire è la unawaited avvertimenti del compilatore - 75 avvisi del compilatore sono un problema di per sé e nascondono avvertimenti reali.

Se si desidera segnalare al compilatore che sei intenzionalmente non facendo nulla con il risultato del compito, è possibile utilizzare un metodo semplice estensione che non fa nulla, ma soddisfa il desiderio del compilatore per esplicitazione.

// Do nothing. 
public static void Release(this Task task) 
{ 
} 

Ora è possibile chiamare

UpdateCacheAsync(data).Release(); 

senza avvisi del compilatore.

https://gist.github.com/lisardggY/396aaca7b70da1bbc4d1640e262e990a

Problemi correlati