2012-05-19 8 views
7

Attualmente sto provando a portare una buona quantità di codice sincrono esistente a WinRT.Quali sono i rischi del wrapping Async/Await IAsyncOperations con il codice Task.Wait()?

Come parte di questo, sto riscontrando problemi con il codice esistente che prevede che alcune operazioni siano sincrone - ad es. per il file I/O

di adattare questo codice esistente per lavorare con l'API stile IAsyncOperation all'interno WinRT, ho usato una tecnica di avvolgere l'IAsyncOperation con un metodo di estensione del tipo:

namespace Cirrious.MvvmCross.Plugins.File.WinRT 
{ 
    public static class WinRTExtensionMethods 
    { 
     public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) 
     { 
      var task = operation.AsTask(); 
      task.Wait(); 
      if (task.Exception != null) 
      { 
       // TODO - is this correct? 
       throw task.Exception.InnerException; 
      } 

      return task.Result; 
     } 
    } 
} 

da MvvmCross WinRT ExtensionMethods - con un metodo simile per IAsyncAction

Questi involucri sembra funzionare - e mi permettono di usare i metodi Async nel codice sincrono come:

public IEnumerable<string> GetFilesIn(string folderPath) 
    { 
     var folder = StorageFolder.GetFolderFromPathAsync(ToFullPath(folderPath)).Await(); 
     var files = folder.GetFilesAsync().Await(); 
     return files.Select(x => x.Name); 
    } 

Capisco che questo non è proprio nello spirito di WinRT; ma mi aspetto che questi metodi vengano normalmente richiamati solo sui thread in background; e sto scrivendo questo con l'obiettivo di rendere il mio codice compatibile multipiattaforma - anche con piattaforme che non supportano ancora attend-async e/o sviluppatori che non sono ancora pronti a fare il salto.

Quindi ... la domanda è: quali rischi sono in esecuzione utilizzando questo tipo di codice?

E come seconda domanda, c'è un modo migliore per ottenere il riutilizzo del codice per aree come I/O file?

+0

http://feedproxy.google.com/~r/AyendeRahien/~3/71OP6uo3bTQ/when-using-the-task-parallel-library-wait-is-a-badwaring-sign –

risposta

2

finalmente sto andando a rispondere a questa ....

E la risposta è non si può davvero farlo.

Anche se si tenta di utilizzare alcuni dei metodi di pulizia suggeriti nelle altre risposte, si ottengono comunque eccezioni se si tenta di eseguire il codice su qualsiasi thread che ha promesso di non bloccare - ad es. se si tenta di eseguire sul thread dell'interfaccia utente o su un thread del threadpool.

Quindi ... la risposta è che devi semplicemente rearchitect quel codice legacy in modo che sia in qualche modo asincrono!

3

In primo luogo, penso che il metodo potrebbe essere riscritto come:

public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) 
{ 
    return operation.AsTask().Result; 
} 

Calling Result sarà sincrono aspettare se il compito non era ancora finito. E lancerà un AgreggateException se fallisce. Penso che buttare lo InnerException come fai tu sia una cattiva idea, perché sovrascrive la traccia dello stack dell'eccezione.

Per quanto riguarda la tua domanda, ritengo che il più grande pericolo nell'usare Wait() insieme al codice asincrono siano deadlock. Se si avvia un'operazione che utilizza internamente await sul thread dell'interfaccia utente e quindi lo si attende utilizzando Wait() sullo stesso thread, si otterrà un deadlock.

Questo non è importante se non si è Wait() sul thread dell'interfaccia utente, ma si dovrebbe comunque evitare se possibile, perché va contro l'intera idea async.

+0

Grazie per il risposta. Per quanto riguarda le eccezioni, sto cercando di far sì che il codice circostante si aspetti eccezioni come FileNotFoundException - e non posso davvero aspettarmi che gestiscano AggregatedException. Nel complesso, tuttavia, questa è * si spera * una mia soluzione temporanea - a lungo termine o riscriverò l'Apis per usare le callback basate sull'azione , o forse userò attesa/async anche nelle altre piattaforme (il supporto è arrivando a MonoTouch e MonoDroid quando vs11/C# 4.5 esce dalla beta!) – Stuart

+0

FWIW, il suo sembra più in linea con la parola chiave await poiché ottiene l'eccezione interna lanciata invece dell'aggregato. –

2

Ci sono molte buone ragioni per non farlo. Si veda ad esempio http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx

Ma se si vuole farlo, utilizzare i getResults() metodo come segue

public static TResult Await<TResult>(this IAsyncOperation<TResult> operation) 
{ 
    try 
    { 
     return operation.GetResults(); 
    } 
    finally 
    { 
     operation.Close(); 
    } 
} 

Avvolgere l'IAsyncOperation in un compito, come svick accennato, funziona ugualmente ma è meno efficiente.

+1

Non ho potuto farlo funzionare. Ho provato il seguente codice: var folder = ApplicationData.Current.LocalFolder; var items = folder.CreateItemQuery(). GetItemsAsync(). Await(); e ricevuto: Un'eccezione di tipo 'System.InvalidOperationException' si è verificata in GetResults. Ulteriori informazioni: Un metodo è stato chiamato in un momento inatteso. (Eccezione da HRESULT: 0x8000000E). AsTask(). Risultato lavorato. –

+0

Ho avuto lo stesso problema ed è il motivo per cui ho trovato questo post. GetResults() non ha funzionato, ma AsTask(). Risultato ha funzionato bene – noggin182

Problemi correlati