2015-05-26 3 views
5

Qualcuno può chiarire questo esempio, che ovviamente, non funziona:metodo C# Async chiamare fino alla principale

class Program 
{ 
    static void Main(string[] args)//main cant' be async 
    { 
     int res = test();//I must put await here 

     Console.WriteLine("END"); 
    } 

    public async static Task<int> test() 
    { //why can't I make it just: public int test()?? 
     int a1, a2, a3, a4; 

     a1 = await GetNumber1(); 
     a2 = await GetNumber2(); 
     a3 = await GetNumber3(); 

     a4 = a1 + a2 + a3; 
     return a4; 
    } 

    public static async Task<int> GetNumber1() 
    { 
     await Task.Run(() => 
      { 
       for (int i = 0; i < 10; i++) 
       { 
        Console.WriteLine("GetNumber1"); 
        System.Threading.Thread.Sleep(100); 
       } 
      }); 
     return 1; 
    } 

che sto cercando di "raccogliere" i valori ottenuti con metodi GenNumberX utilizzando "attendono". Mi piacerebbe rendere il metodo "test" non asincrono in qualche modo. Non capisco perché il test deve essere asincrono quando sto usando attendi per ottenere un valore. Questo esempio mi fa scrivere asincronamente su ogni metodo in cui utilizzo async, e quando eseguo il drill su Main, non riesco a renderlo asincrono?

come scrivere esempio del mondo reale:

public bool ReserveAHolliday() 
{ 
     bool hotelOK = await ReserveAHotel();//HTTP async request 
     bool flightOK = await ReserveFlight();////HTTP async request 
     bool result = hotelOK && flightOK; 
     return result; 
} 

Come rendere metodo ReserveAHolliday sincrono? Mi manca qualcosa o non capisco l'uso del meccanismo di attesa asincrono?

+1

A un certo punto è necessario avviare un asynctask da un metodo sincrono. Se vuoi sparare, chiamalo semplicemente (chiamato anche vuoti asincroni, accendi e dimentichi), o usa 'test(). Wait()' che attenderà l'attività in modo sincrono. – FrankerZ

risposta

2

di seguito è un esempio completo. è possibile eseguire ReserverAHoliday in modo sincrono (bool r = ReserveAHolliday(). Result;) e Asynchronously (basta chiamare ReserveAHolliday();) da MAIN (dipende da quale linea si commenta). e puoi vedere l'effetto ("END" viene stampato prima/dopo aver completato la prenotazione). Preferisco i metodi attendi Task.WhenAll(), che è più leggibile. nota anche che è preferibile utilizzare Attendi Task.Delay (100) anziché Thread.sleep all'interno di GetNumber1.

class Program 
{ 
    static void Main(string[] args)//main cant' be async 
    { 
     //int res = test().Result;//I must put await here 
     bool r = ReserveAHolliday().Result; //this will run Synchronously. 
     //ReserveAHolliday(); //this option will run aync : you will see "END" printed before the reservation is complete. 
     Console.WriteLine("END"); 
     Console.ReadLine(); 
    } 

    public async static Task<int> test() 
    { //why can't I make it just: public int test()?? 
     //becuase you cannot use await in synchronous methods. 
     int a1, a2, a3, a4; 

     a1 = await GetNumber1(); 
     a2 = await GetNumber1(); 
     a3 = await GetNumber1(); 

     a4 = a1 + a2 + a3; 
     return a4; 
    } 

    public static async Task<int> GetNumber1() 
    { 
     //await Task.Run(() => 
     // { 
       for (int i = 0; i < 10; i++) 
       { 
        Console.WriteLine("GetNumber1"); 
        await Task.Delay(100); // from what I read using Task.Delay is preferred to using System.Threading.Thread.Sleep(100); 
       } 
     // }); 
     return 1; 
    } 

    public async static Task<bool> ReserveAHolliday() 
    { 
     //bool hotelOK = await ReserveAHotel();//HTTP async request 
     //bool flightOK = await ReserveAHotel();////HTTP async request 
     var t1 = ReserveAHotel("FirstHotel"); 
     var t2 = ReserveAHotel("SecondHotel"); 
     await Task.WhenAll(t1, t2); 
     bool result = t1.Result && t1.Result;// hotelOK && flightOK; 
     return result; 
    } 
    public static async Task<bool> ReserveAHotel(string name) 
    { 
     Console.WriteLine("Reserve A Hotel started for "+ name); 
     await Task.Delay(3000); 
     if (name == "FirstHotel") 
      await Task.Delay(500); //delaying first hotel on purpose. 
     Console.WriteLine("Reserve A Hotel done for " + name); 
     return true; 
    } 
} 
+1

Grazie Guy. Questo esempio può creare deadlock, come @Kabbalah menzionato nella risposta sopra? Probabilmente dovrei mantenere quei metodi asincroni, dal momento che sto per scrivere una libreria da utilizzare nel progetto WinForms. Questo codice era solo un esempio. In realtà sto scrivendo SignalR client/wrapper per abilitare la comunicazione tra vari software (ERP-> POS, POS-> ERP) con metodi quali: _IsUserOnline (stringa userId) _, _GetOnlineUsers_, ecc. – vpetrovic

+0

In WinForms, probabilmente ... dipende su cosa succede esattamente nel tuo grimorio. Il tuo codice libreria richiede un contesto di sincronizzazione? Se la risposta è sì, sicuramente si bloccherà.Se la risposta è no, si potrebbe farla franca se si specifica ConfigureAwait (false) nel codice della libreria. Modificherò la mia risposta con ulteriori informazioni. – Kabbalah

+0

Suggerisco di trascorrere del tempo su questo, in modo da non bruciare. Penso che se si utilizza attendere, si è più sicuri, perché si è in un contesto asincrono, e il codice sincrono che inizialmente chiamato il metodo asincrono può continuare il suo lavoro ... per essere più sicuro di implicazione Suggerisco di aggiungere un lungo ritardo all'interno del tuo async, in modo da poter vedere l'impatto sul comportamento per te. Inoltre, preferisco lanciare tale async da una proprietà booleana (propfull) che io uso come flag (in modo che lo stesso task non venga lanciato più volte in parallelo, quando non lo voglio). Non so se questo vale per te e se è una buona pratica. – Guy

1

Il tuo async deve iniziare da qualche parte, e poiché il tuo programma ha un punto di partenza, è un metodo sincrono a questo punto. La maggior parte degli async inizia da ciò che ci piace chiamare async void, che sono fondamentalmente metodi ignifughi e dimenticati. Inizia un'attività e non gli importa cosa restituisce. Se è necessario attendere qualcosa in un metodo sincrono, è possibile utilizzare il metodo .Wait() in un'attività o utilizzare .Result per ottenere il risultato dell'attività.

Per il vostro esempio del mondo reale, se si desidera eseguire entrambi i compiti allo stesso tempo, si avrebbe bisogno di fare qualcosa di simile:

public async Task<bool> ReserveAHoliday() 
{ 
     //Initialize and start this task 
     Task<bool> hotelTask = ReserveAHotel();//HTTP async request 
     //Initialize and start this task 
     Task<bool> flightTask = ReserveFlight();////HTTP async request 
     //Await until hotel task is done, and get the bool 
     bool hotelOK = await hotelTask; 
     //Await until flight task is done (This might have already finished while the hotel was grabbing, and get the bool 
     bool flightOK = await flightTask; 
     bool result = hotelOK && flightOK; 
     //Return the final result 
     return result; 
} 

mi raccomando guardare this video. Fornisce una buona introduzione a come funziona Async e può farvi iniziare nel meraviglioso mondo di Async.

+0

_ReserveAHolliday_ non funzionerà a causa dell'uso di _await_ senza dichiarare un metodo async. Quindi sembra che devo usare il metodo _Task.Wait() _. – vpetrovic

+0

Ho aggiornato l'esempio. ReserveAHoliday dovrebbe essere un'attività asincrona. Come ho detto, ti consiglio vivamente di dare un'occhiata al video che ho linkato. Ci vorrà un po 'di tempo, ma ne vale la pena. – FrankerZ

0

Grazie FrankerZ per la risposta giusta! Quindi il mio esempio litle dovrebbe essere simile a questo:

class Program 
{ 
    static void Main(string[] args) 
    { 
     var task = test(); 
     task.Wait(); //this stops async behaviour 
     int result = task.Result;// get return value form method "test" 
     Console.WriteLine("RESULT IS = " + result); 
    } 

    public async static Task<int> test() 
    { 
     int a1, a2, a3, a4; 

     //run all tasks 
     //all tasks are doing their job "at the same time" 
     var taskA1 = GetNumber1(); 
     var taskA2 = GetNumber2(); 
     var taskA3 = GetNumber3(); 

     //wait for task results 
     //here I am collecting results from all tasks 
     a1 = await taskA1; 
     a2 = await taskA2; 
     a3 = await taskA3; 

     //get value from all task results 
     a4 = a1 + a2 + a3; 
     return a4; 
    } 

    public static async Task<int> GetNumber1() 
    { 
     await Task.Run(() => 
      { 
       for (int i = 0; i < 10; i++) 
       { 
        Console.WriteLine("GetNumber1"); 
        System.Threading.Thread.Sleep(100); 
       } 
      }); 
     return 1; 
    } 

// altri metodi sono ommited perché sono le stesse come GetNumber1 }

+1

'Task.Result' blocca fino a quando l'attività non è completa, quindi non è necessario' task.Wait() ' –

+0

Attenzione, perché funziona solo in un'applicazione console. Se provi questo in un'applicazione WPF o Forms, molto probabilmente causerà un deadlock. Si prega di consultare il link nel mio post su questo problema. – Kabbalah

+0

Non usare 'Thread.Sleep (100)' - usa invece 'attende Task.Delay (100)'. –

0

Per poter utilizzare la parola chiave await all'interno di un metodo, che ha essere async. Questa è stata una decisione progettuale per rendere più facile la retrocompatibilità.

Main non può essere async poiché è il punto di partenza del programma, quindi in genere è necessario bloccare in qualche modo per mantenere il programma in esecuzione. È possibile farlo bloccando su un Task o anche chiamando Console.ReadLine.

Operazioni di solito vengono eseguiti su una priorità di thread e thread in background non mantenere il vostro programma in esecuzione, se tutte le discussioni in primo piano si fermano.

Ho un messaggio introduttivo blog sul async-awaithere

0

Il metodo ReserveAHolliday non può essere fatta sincrono se si utilizza la parola chiave Await all'interno di esso.

Che cosa potrebbe essere possibile è quello di bloccare semplicemente fino a entrambi i metodi asincroni restituiscono il loro valore. Non lo consiglio però perché potrebbe portare a deadlock o altri errori imprevisti.

Se non si dispone di un contesto di sincronizzazione (applicazione per es. Console) si può semplicemente scrivere

bool hotelOK = ReserveAHotel().Result; 

Ma mi sa che in ultima analisi desidera creare una GUI. È qui che il blocco cade a pezzi. Poiché l'applicazione dell'interfaccia utente (Forms e WPF) ha un contesto e l'attesa per i metodi asincroni causa deadlock. Read more here

Esistono soluzioni alternative per questo problema, ma dipendono fortemente dalla progettazione dell'applicazione.

Edit:

Se il codice della libreria ha bisogno di sincronizzazione Contesto quindi il blocco sarà certamente causare un deadlock.

Se non è necessario, è possibile utilizzare ConfigureAwait(false) nel codice della libreria e ottenere via con il blocco.

Quello che consiglio è: async/attendere fino in fondo o no async/attendere affatto. Ci sono altre possibilità per rendere il tuo codice asincrono, ad esempio: basato su eventi, ma come tutto nella vita ha pro e contro.

Leggere this blog da Stephen Cleary. Spiega perché bloccare il codice asincrono si bloccherà e come evitarlo.

+0

Sì, sto scrivendo un progetto di libreria, che verrà utilizzato nella GUI di WinForms. Se questo può causare qualche problema, manterrò questi metodi _async_, non è un grosso problema, volevo solo progettare quelle classi nel modo in cui avevo pianificato, ma questo piano potrebbe essere sbagliato. – vpetrovic