2015-08-13 9 views
5

Come sperimentare il nuovo async & Attendere le funzionalità di 4.5 Voglio cancellare alcune confusioni prima di andare oltre. Ho letto diversi articoli e anche diverse domande su SO e mi aiuta a capire come funziona Async e Await. Cercherò solo di mettere qui la mia comprensione e confusione e apprezzerò se qualcuno codice educare me e altre persone che stanno cercando le stesse cose. Ne parlo in parole molto semplici.Async attende poche confusioni

Quindi, Async viene utilizzato in modo che il compilatore sappia che il metodo contrassegnato da Async contiene l'operazione In attesa (operazione lunga). L'ultimo framework contiene diversi nuovi metodi incorporati per le operazioni asincrone.

Async incorporato funziona come connection.OpenAsync, ExecuteScalarAsync ecc. Vengono utilizzati con la parola chiave Await. Non conosco il funzionamento interno di questi metodi asincroni, ma la mia forte ipotesi è che sotto il cofano stanno usando Task.

Posso fare come regola generale che Await sarà con qualsiasi metodo che implementa Task. Quindi, se ho bisogno di creare il mio metodo che sta eseguendo un'operazione lunga, lo creerò come Task e quando sarà chiamato userò Await Keyword con esso?

La seconda cosa più importante è che la regola generale è creare un metodo Async o crearlo come compito. Ad esempio,

public void SampleMain() 
{ 
    for (int i = 1; i <= 100; i++) 
    { 
     DataTable dt = ReadData(int id); 
    } 
} 

public DataTable ReadData(int id) 
{ 
    DataTable resultDT = new DataTable(); 

    DataTable dt1 = new DataTable(); 
    // Do Operation to Fill DataTable from first connection string 
    adapter.Fill(dt1); 

    DataTable dt2 = new DataTable(); 
    // Do Operation to Fill DataTable from first connection string 
    adapter.Fill(dt2); 

    // Code for combining datatable and returning the resulting datatable 
    // Combine DataTables 
    return resultDT; 
} 

public string GetPrimaryConnectionString() 
{ 
    // Retrieve connection string from some file io operations 
    return "some primary connection string"; 
} 

public string GetSecondaryConnectionString() 
{ 
    // Retrieve connection string from some file io operations 
    return "some secondaryconnection string"; 
} 

Ora questo è uno scenario molto semplice che ho creato in base ad alcune applicazioni del mondo reale in cui ho lavorato in passato. Quindi mi stavo chiedendo come rendere questo intero processo Async.

Devo rendere GetPrimaryConnectionString e GetSecondaryConnectionString come attività e attenderli in ReadData. ReadData sarà anche un compito? Come chiamare ReadData nella funzione SampleMain?

Un altro modo potrebbe essere quello di creare un'attività per ReadData in SampleMain ed eseguire tale attività e saltare la conversione di altri metodi come Attività. E 'questo il buon approccio? Sarà veramente Asincrono?

+0

Esempio di cosa intendi "creare un metodo come un'attività" sarebbe più utile di un codice sincrono un po 'casuale che hai aggiunto al post. –

+0

@Alexei Levenkov Grazie per la risposta. Non capisco il tuo commento completamente. Quello che ho presentato è uno scenario campione di esempio con meno codice possibile per far capire ai lettori come rendere questo intero processo Asincrono usando Async e Await. Non è affatto casuale. –

+0

@ M.kazem Akhgary. Tutto apposto. Quindi non c'è bisogno di cambiare i metodi GetPrimaryConnectionString e GetSecondaryConenctionString. Se faccio solo il ReadData come Async tutto questo processo sarà Asincrono? –

risposta

6

Così asincrona viene utilizzato in modo che il compilatore sa che il metodo segnato da Async contiene operazione attendono

Il async viene utilizzato in modo che il compilatore avrà un'indicazione di creare uno stato macchina fuori dalla metodo. Un metodo async non può avere await e funziona ancora, anche se verrà eseguito completamente in modo sincrono.

Le funzioni asincrone builtin come connection.OpenAsync, ExecuteScalarAsync ecc sono utilizzati con attendono parola chiave. Non conosco il funzionamento interno di questi metodi Async ma la mia forte ipotesi è che sotto il il cappuccio stanno usando Attività.

Task è una promessa di lavoro da completare in futuro. Ci sono un paio di modi per creare un Task. Ma, Task non è l'unica cosa che può rappresentare un'operazione asincrona. È possibile creare un attendibile se si desidera, tutto ciò che serve per implementare un metodo GetAwaiter che restituisce un tipo che implementa INotifyCompletion.

Se si desidera sapere come viene implementato un metodo nel framework, you can view the source.In questo caso particolare, usano TaskCompletionSource<T>.

Devo fare GetPrimaryConnectionString e GetSecondaryConnectionString come Tasks e li attendono in ReadData. ReadData è anche un compito? Come chiamare ReadData nella funzione SampleMain ?

Non c'è nulla di intrinsecamente asincrono sul recupero di una stringa di connessione. Di solito (non sempre) si utilizza async-await con operazioni IO asincrone naturalmente. In questo caso particolare, l'unica operazione asincrona effettiva è ReadData e, se si desidera renderla asincrona, è possibile utilizzare SqlDataReader, che espone i metodi asincroni.

Un esempio, tratto dalla ADO.NET teams blog:

public static async Task<Product> GetProductAndReviewsAsync(
      int productID, int reviewsToGet) 

{ 
    using (SqlConnection connection = new SqlConnection(ConnectionString)) 
    { 
     await connection.OpenAsync(); 
     const string commandString = GetProductByIdCommand + ";" 
            + GetProductReviewsPagedById; 

     using (SqlCommand command = new SqlCommand(commandString, connection)) 
     { 
      command.Parameters.AddWithValue("productid", productID); 
      command.Parameters.AddWithValue("reviewStart", 0); 
      command.Parameters.AddWithValue("reviewCount", reviewsToGet); 
      using (SqlDataReader reader = await command.ExecuteReaderAsync()) 
      { 
       if (await reader.ReadAsync()) 
       { 
        Product product = GetProductFromReader(reader, productID); 
        if (await reader.NextResultAsync()) 
        { 
         List<Review> allReviews = new List<Review>(); 
         while (await reader.ReadAsync()) 

         { 
          Review review = GetReviewFromReader(reader); 
          allReviews.Add(review); 
         } 
         product.Reviews = allReviews.AsReadOnly(); 
         return product; 
        } 
        else 
        { 
         throw new InvalidOperationException(
          "Query to server failed to return list of reviews"); 
        } 
       } 
       else 
       { 
        return null; 
       } 
      } 
     } 
    } 
} 
+0

Per me la risposta ora è perfetta con l'edizione del codice di esempio. Grazie –

+0

Nel codice di esempio che hai condiviso con me viene menzionato un oggetto nullable esplicito all'interno dell'attività come Task . Questo deve essere esplicito? –

+0

@AdnanYaseen Qualsiasi tipo di ritorno da un'operazione asincrona deve restituire un 'Task' o un' Task '. Ho rimosso il 'Prodotto? 'Per non confondervi. –

1

come rendere tutto questo processo asincrona

Se ci sono versioni asincrone di adapter.Fill, poi semplicemente await per essa in ReadData, che a sua volta diventa anche async e puoi aspettarlo nel metodo del chiamante:

// in async button click event 
button.Enabled = false; 
var dt = await ReadData(int id); 
button.Enabled = true; 
... // do something with dt 

public async Task<DataTable> ReadData(int id) 
{ 
    ... 
    var job1 = adapter.AsyncFill(dt1); 
    var job2 = adapter.Fill(dt2); 
    // wait for all of them to finish 
    Task.WaitAll(new[] {job1, job2}); 
    ... 
    return Task.FromResult(resultDT); // dump approach 
} 

se non ci sono versione asincrona allora si necessario creare loro (utilizzando Task):

// in async button click event 
button.Enabled = false; 
// run synchronous task asynchronously 
var dt = await Task.Run(() => ReadData(int id)); 
button.Enabled = true; 
... // do something with dt 

async/await brilla quando si tratta di interfaccia utente, in caso contrario (se alcuna interfaccia utente è coinvolto) basta creare compito ed eseguire operazioni sincrone lì.

+0

'async-await' non ha nulla a che fare con l'interfaccia utente. * * Aiuta * ad usarlo quando si hanno operazioni IO non bloccanti. Ma usando 'Task.Run' come quello non si scala. –

+0

@Sinatr Grazie per avermi fornito un codice di esempio che mi aiuta a capire meglio. Solo una domanda veloceHai detto che se l'interfaccia utente non è coinvolta, devo solo creare un'attività ed eseguirla in modo sincrono. Cosa succede se non ho un'interfaccia utente coinvolta e devo eseguire attività parallele? –

+0

@AdnanYaseen, non penso che guadagnerai nulla se spieghi che 'for' in TPL. Il collo di bottiglia è o HDD o rete. Se fai qualche elaborazione pesante (che è paragonabile al tempo necessario per ricevere i dati), allora sicuramente ti sarà d'aiuto. Io stesso non ho mai dovuto fare tali calcoli, quindi la mia risposta riguarda l'interfaccia utente. – Sinatr

0

L'unica ragione per utilizzare async-await è se il thread principale potrebbe fare qualcosa di utile mentre un altro thread sta facendo l'operazione di lunghezza. Se il thread principale avvia l'altro thread e attende solo che l'altro thread finisca, è meglio lasciare che il thread principale esegua l'azione.

Una delle cose che un thread principale spesso fa è mantenere l'interfaccia utente reattiva.

Hai ragione, sotto il cofano async-attese utilizza Attività, quindi si vede che una funzione asincrona restituisce un'attività.

Le regole:

  • Se una funzione sarebbe tornato vuoto, la versione asincrona torna Task. Se la funzione restituisse TResult, la versione asincrona dovrebbe restituire l'attività <TResult>.
  • C'è un'eccezione: il gestore di eventi asincroni restituisce void.
  • Il valore di ritorno di Attesa Attività è nullo. Il valore di ritorno dell'attesa Task <TResult> è TResult.
  • Solo le funzioni asincrone possono chiamare altre funzioni asincrone.
  • Se si dispone di una funzione non asincrona, è comunque possibile utilizzare la funzione asincrona. Comunque non puoi usare Attendere. Utilizzare il valore restituito dall'attività della funzione asincrona e i metodi System.Threading.Tasks.Task per attendere i risultati.
  • Se si dispone di una funzione asincrona e desidera avviare una funzione non asincrono in un thread separato, uso:

    private int SlowCalculation (int a, int b) { System.Threading.Thread.Sleep (TimeSpan.FromSeconds (5)); return a + b; }

    privato asincrona Task CalculateAsync (int a, int b) { Task MyTask = Task.Run (() => SlowCalculation (a, b); // mentre SlowCalcuation sta calcolando lentamente, fanno altro utile cose // dopo un po 'è necessario l'int somma risposta = attendere MyTask; somma di ritorno;. }

vedere che il ritorno di attendere Task <int> è int

Alcune persone usavano funzioni come Task.ContinueWith. A causa dell'attesa dichiarazione che non è più necessaria. Attendi che l'attività sia terminata. La dichiarazione dopo l'attesa è ciò che normalmente faresti in ContinueWith.

In Task.ContinueWith potresti dire: "fallo solo se l'attività non è riuscita". L'equivalente atteso da async per questo è try-catch.

Ricordate: se il filo non ha nulla di utile da fare (come mantenere l'interfaccia utente sensibile), non utilizzare async-attendere

Avvio di più compiti in async-await e in attesa di loro di finire Procedere come segue:

private async Task MyAsyncFunction(...) 
{ 
    var tasks = new List<Task<int>>(); 
    for (int i=0; i<10; ++i) 
    { 
     tasks.Add(CalculateAsync(i, 2*i); 
    } 
    // while all ten tasks are slowly calculating do something useful 
    // after a while you need the answer, await for all tasks to complete: 
    await Task.WhenAll(tasks); 
    // the result is in Task.Result: 
    if (task[3].Result < 5) {...} 
} 

La versione asincrona-attesa di Task.Waitall è Task.WhenAll. QuandoTutti restituisce un'attività invece di nulla, quindi puoi attendere. Il thread principale rimane reattivo anche in attesa.

Il thread principale non è il caso quando si utilizza Task.WaitAll, perché non si attende.