2012-07-03 18 views
24

Próbuję załadować i odczytać plik ustawień po uruchomieniu aplikacji, a około 90% czasu, await GetFileAsync("filename.xml"); nigdy nie zwraca, a więc zawiesza aplikację .Zadzwoń, aby czekać na GetFileAsync() nigdy nie wraca i aplikacja zawiesza się w aplikacji WinRT

Około jedna czwarta czasu, jeśli przejdę przez ten kod, wróci on i odczyta plik.

Oto bardzo uproszczona wersja kodu:

App.xaml.cs:

protected override void OnLaunched(LaunchActivatedEventArgs args) 
{ 
    FileLoader.Load().Wait(); 

    // File-load dependent stuff 
} 

FileLoader.cs:

public async static Task Load() 
{ 
    StorageFolder folder = ApplicationData.Current.LocalFolder; 
    StorageFile file; 
    bool fileExists = true; 

    try 
    { 
     // The following line (often) never returns 
     file = await folder.GetFileAsync("filename.xml"); 
    { 
    catch 
    { 
     fileExists = false; 
    } 

    // Do stuff with loaded file 
} 

Gdybym obserwować okno Output w Visual Studio , po chwili oczekiwania dostaję "The thread '<No Name>' (0x30c) has exited with code 0 (0x0)."

Czy ktoś ma jakiś pomysł co tu się dzieje?

+6

Blokujesz wątek interfejsu użytkownika, o ile widzę, dzwoniąc do 'Czekaj'. To naprawdę zły pomysł. Heck, to może być przyczyna problemu. –

+1

Myślę, że Jon ma całkowitą rację - czy masz szansę zaznaczyć OnLaunched jako async, a następnie poczekać na wywołanie Load()? Jeśli nie możesz, innym rozwiązaniem może być ładowanie, aby wykonać działanie po zakończeniu obciążenia, a następnie uruchom działanie na końcu (lub, oczywiście, możesz użyć metody ContinueWith na zadaniu, które Load() zwraca tak jak przed zrobieniem/czekaniem :) –

+0

Obaj mieliście rację, Jon i James. Dziękuję bardzo za wkład! Pierwotnie używałem 'Wait', ponieważ nie zdawałem sobie sprawy, że możesz dodać' async' do przesłoniętej metody i nie wiedziałem, że 'OnLaunched' działa w wątku UI. Usunięcie 'Wait' i dodanie' async' do 'OnLaunched' naprawiło to! Chciałbym móc zaznaczyć twoje komentarze jako odpowiedź. – jokeefe

Odpowiedz

47

Domyślnie, jeśli nie zostało jeszcze wykonane, metoda wznawia przechwycony kontekst (w tym przypadku kontekst interfejsu użytkownika).

Tak, oto dlaczego Twój kod się niepowodzeniem:

  • OnLaunched rozmowy Load (w kontekście UI).
  • Load czeka. Powoduje to, że metoda Load zwraca niepełne zadanie i planuje jego ukończenie na później. Ta kontynuacja jest zaplanowana w kontekście interfejsu użytkownika.
  • OnLaunched bloki w zadaniu zwróconym od Load. To blokuje wątek interfejsu użytkownika.
  • GetFileAsync ostatecznie kończy i próbuje uruchomić kontynuację dla Load.
  • Kontynuacja dla Load czeka, aż wątek interfejsu użytkownika będzie dostępny, aby mógł być uruchamiany w kontekście interfejsu użytkownika.
  • W tym momencie OnLaunched czeka na zakończenie Load (blokując wątek interfejsu użytkownika), a Load czeka na wątek UI, aby był wolny. Impas.

Te najlepsze praktyki uniknąć tej sytuacji:

  1. W swojej "biblioteki" async metod użyć ConfigureAwait(false) miarę możliwości. W twoim przypadku zmieniłoby to await folder.GetFileAsync("filename.xml"); na await folder.GetFileAsync("filename.xml").ConfigureAwait(false);.
  2. Nie blokuj na Task s; to jest async do samego końca. Innymi słowy, zamień Wait na await.

Aby uzyskać więcej informacji:

Update, 2012-07-13: Incorporated tę odpowiedź into a blog post.

+0

Doskonała odpowiedź! Bardzo dziękuję za szczegółowe wyjaśnienie, co się dzieje i obszerną listę artykułów referencyjnych. Dzięki temu wszystko staje się bardziej przejrzyste. – jokeefe

+0

To było idealne. Uratowało mnie to tyle żalu, że było to niewiarygodne. – ohmusama

+1

Chciałabym, żeby stwardnienie rozsiane umarło. Zdecydowanie szukają ludzi ze względu na dostarczanie tak szalonych rzeczy. Dlaczego, do diabła, po prostu nie zostawili wątków staromodnych i nie zsynchronizowali prymitywów ?! Morons. –

Powiązane problemy