2016-01-20 30 views
5

Ho appena, per una bozza di massima, convertito un intero lotto di metodi di accesso ai dati in async usando il seguente schema, e sembra troppo semplice per essere sufficiente per le iterazioni successive. Quanto è sicuro, cosa manca e come dovrei farlo?Le chiamate a esecuzione prolungata sono così semplici?

Il servizio che fornisce le chiamate di lunga durata:

private class UserService 
{ 
    public IdentityUser GetById(int id) 
    { 
     ... 
    } 
} 

private UserService _userService = new UserService(); 

Il metodo sincrono originale:

public IdentityUser GetById(int id) 
{ 
    return _userService.GetById(id); 
} 

mio fantastico nuovo metodo asincrono:

public async Task<IdentityUser> GetByIdAsync(int id) 
{ 
    await Task.Run(() => _userService.GetById(id)); 
} 
+0

Le query di lunga durata contengono un lungo elenco di possibili problemi che è necessario affrontare. Perché non installare solo il SignalR nuget e far sì che tutti siano riparati per te - e anche il supporto websocket! –

+0

@ MattiasÅslund: persone diverse hanno definizioni diverse di "long-running". Ho la sensazione che GetById() non sia il tipo di "long-running" che meriterebbe l'uso di SignalR. – StriplingWarrior

+0

È qualcosa che si consuma come servizio WCF o Web API? Se è così, peggiori le cose solo dal lato server. – Noseratio

risposta

5

Si consiglia di non fare " falso async "metodi come questo:

Il motivo per cui lo chiamo "falso asincrono" è perché non c'è nulla di intrinsecamente asincrono sull'operazione. In questo caso, dovresti avere solo il metodo sincrono. Se un chiamante vuole renderlo asincrono usando Task.Run, può farlo.

Quando è qualcosa di intrinsecamente asincrono? Quando si effettua una richiesta a un servizio Web oa un database, ad esempio, tra l'invio della richiesta e la ricezione della risposta vi è un periodo di attesa: la richiesta è un'operazione intrinsecamente asincrona. Per evitare di bloccare il thread chiamante, utilizzare async-await.

+0

Devo implementare il metodo esattamente come 'public async Task GetByIdAsync (int id)', che non verrà compilato se non faccio diventare la sua operazione interna un async falso, e 'UserService' non offre metodi asincroni. – ProfK

+2

@ProfK, quindi implementarlo come restituito 'Task.FromResult' e senza la parola chiave' async' (che non fa comunque parte della firma del metodo compilato). – Noseratio

+1

@Noseratio Mi piace se mi piace 'Task.FromResult', e lo ho usato in molti posti in cui sono stato costretto a restituire un' Task' o anche 'await' uno. Penso che potrei giocare sul sicuro e far sì che tutti i miei 'falsi' lo usino. – ProfK

2

Tecnicamente, che funziona, ma funziona creando un nuovo thread per eseguire un'operazione sincrona, che a sua volta è avvolgente e blocco su un un'operazione intrinsecamente asincrona. Ciò significa che non stai ottenendo alcuni dei maggiori vantaggi di andare async in primo luogo.

Il modo corretto è procedere in modo asincrono fino in fondo. Mentre in questo momento, probabilmente avete qualcosa di simile:

private class UserService 
{ 
    public IdentityUser GetById(int id) 
    { 
     return mContext.Users.Single(u => u.Id == id); 
    } 
} 

... si dovrebbe ora creare una versione asincrona:

private class UserService 
{ 
    public async Task<IdentityUser> GetByIdAsync(int id) 
    { 
     return await mContext.Users.SingleAsync(u => u.Id == id); 
    } 
} 

Usage:

public async Task<IdentityUser> GetByIdAsync(int id) 
{ 
    return await _userService.GetByIdAsync(id); 
} 

Supponendo, naturalmente, che il tuo framework sottostante supporta metodi asincroni come SingleAsync() per operazioni intrinsecamente asincrone, questo consentirà al sistema di rilasciare il thread corrente mentre aspetti i databas e operazione da completare. Il thread può essere riutilizzato altrove, e quando l'operazione è terminata, puoi usare qualunque thread sia disponibile in quel momento.

Probabilmente vale anche la pena leggere e adottare these Best Practices. Probabilmente vorrai utilizzare .ConfigureAwait(false) ovunque tu non stia accedendo a informazioni contestuali come sessioni e richieste, per esempio.

Questa risposta presuppone, naturalmente, che GetById sia intrinsecamente asincrono: lo si sta recuperando da un disco rigido o da un percorso di rete o qualcosa del genere. Se sta calcolando l'ID dell'utente utilizzando un'operazione CPU a esecuzione prolungata, quindi Task.Run() è un buon modo per andare, e probabilmente vorrai specificare ulteriormente che si tratta di un'attività a esecuzione prolungata negli argomenti a Task.Run().

+0

Non sto calcolando 'id', sto cercando un utente in un DB con un determinato ID, e forse il DB ha milioni di utenti e nessun indice su' Id' – ProfK

+0

@ProfK: Dal back-end del tuo database non supporta le attività asincrone, perché stai cercando di rendere il metodo 'GetByIdAsync'' async' in primo luogo? Se è così che il chiamante può spostarsi su un'altra attività mentre questo è in esecuzione, quindi rendere la chiamata del chiamante per chiamare 'Task.Run()'. (vedi http://blog.stephencleary.com/2013/11/taskrun-etiquette-examples-dont-use.html) Stai semplicemente rendendo il tuo codice a prova di futuro, nella speranza che un giorno questo sarà asincrono? In tal caso, 'restituisci Task.FromResult (_userService.GetById (id));'. Altrimenti, non c'è motivo di usare 'async' affatto, quindi non farlo. – StriplingWarrior

0

Task.Run() deve essere utilizzato solo per il lavoro con CPU. Non ricordo bene perché, però. Provare a creare invece un metodo GetByIdAsync() che alla fine chiama una risorsa asincrona.

+0

Non ho una risorsa asincrona, la risorsa essendo NHibernate, che per quanto ho scoperto non supporta ancora asincrono. – ProfK