2012-05-19 13 views
7

Aktualnie próbuję przenieść do WinRT sporą ilość istniejącego kodu synchronicznego.Jakie są zagrożenia związane z pakowaniem Async/Oczekiwanie na IAsyncOperations za pomocą kodu Task.Wait()?

W związku z tym mam problem z istniejącym kodem, oczekując synchronizacji niektórych operacji - np. dla pliku I/O

Aby przystosować ten istniejący kod do pracy ze stylem API IAsyncOperation w WinRT, Użyłem techniki owijania IAsyncOperation z metodę rozszerzenia jak:

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; 
     } 
    } 
} 

z MvvmCross WinRT ExtensionMethods - o podobnym sposobie IAsyncAction

te opakowania wydaje się działać - i one pozwalają mi korzystać z Async metody w kodzie synchronicznym jak:

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

Rozumiem, że tak naprawdę nie jest to zgodne z duchem WinRT; ale spodziewam się, że te metody będą normalnie wywoływane tylko w wątkach tła; piszę o tym, aby mój kod był kompatybilny z różnymi platformami - w tym na platformach, które jeszcze nie obsługują oczekujących asynchronicznie i/lub programistów, którzy nie są jeszcze gotowi do wykonania skoku.

A więc ... pytanie brzmi: jakie zagrożenia wiążę się z używaniem tego typu kodu?

Co do drugiego pytania, czy istnieje lepszy sposób na ponowne wykorzystanie kodu w obszarach takich jak File I/O?

+0

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

Odpowiedz

2

mi wreszcie odpowie na to ....

A odpowiedź jest naprawdę nie można zrobić.

Nawet jeśli spróbujesz zastosować niektóre metody czystsze sugerowane w innych odpowiedziach, to ostatecznie trafisz w wyjątki, jeśli spróbujesz uruchomić kod w dowolnym wątku, który obiecał nie blokować - np. jeśli próbujesz uruchomić wątek interfejsu użytkownika lub wątek wątku.

Więc ... odpowiedź polega na tym, że musisz ponownie dokonać rearchitekcji tego starszego kodu, aby był w pewien sposób asynchroniczny!

3

Po pierwsze, myślę, że metoda może być zapisane jako:

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

Wywołanie Result będzie synchronicznie czekać, jeśli zadanie nie zostało jeszcze skończyć. I wyrzuci AgreggateException, jeśli zawiedzie. Myślę, że rzucanie InnerException tak jak ty to zły pomysł, ponieważ nadpisuje ślad stosu wyjątku.

Jeśli chodzi o twoje aktualne pytanie, myślę, że największym niebezpieczeństwem przy używaniu Wait() wraz z kodem asynchronicznym są zakleszczenia. Jeśli uruchomisz operację, która wewnętrznie używa await w wątku interfejsu użytkownika, a następnie czekasz na jej użycie w tym samym wątku za pomocą Wait(), pojawi się zakleszczenie.

To nie jest tak ważne, jeśli nie jesteś Wait() na wątku UI, ale powinieneś go unikać, jeśli to możliwe, ponieważ jest to sprzeczne z całym pomysłem async.

+0

Dzięki za odpowiedź. W przypadku wyjątku próbuję sprawić, aby otaczający kod oczekiwał wyjątków, takich jak FileNotFoundException - i nie mogę naprawdę oczekiwać, że będą one obsługiwać wyjątki AggregatedException. Ogólnie rzecz biorąc, jest to * miejmy nadzieję * rozwiązanie typu "stopgap" ode mnie - w dłuższej perspektywie będę albo przepisywać Apisa do użycia wywołań zwrotnych opartych na Action , albo może będę korzystać z oczekiwania/asynchronizacji na innych platformach (wsparcie jest przyjście do MonoTouch i MonoDroid, gdy wersja beta 11/C# 4.5 wychodzi z wersji beta!) – Stuart

+0

FWIW, jego wydaje się być bardziej zgodna z oczekiwanym słowem kluczowym, ponieważ generuje wewnętrzny wyjątek zamiast agregatu. –

2

Istnieje wiele dobrych powodów, aby tego nie robić. Patrz na przykład http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx

Ale jeśli chcesz to zrobić, użyj getresults() metoda następująco

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

Owijanie IAsyncOperation w zadaniu, jak svick wspomniano, działa też, ale jest mniej skuteczny.

+1

Nie mogłem tego uruchomić. Próbowałem następujący kod: var folder = ApplicationData.Current.LocalFolder; var items = folder.CreateItemQuery(). GetItemsAsync(). Await(); i dostałem: W narzędziu GetResults wystąpił wyjątek typu "System.InvalidOperationException" . Więcej informacji: Metoda została wywołana w nieoczekiwanym czasie. (Wyjątek od HRESULT: 0x8000000E). AsTask(). Wynik zadziałał. –

+0

Miałem ten sam problem i dlatego znalazłem ten post. Funkcja GetResults() nie działała, ale AsTask(). Wynik działał poprawnie – noggin182

Powiązane problemy