2010-10-30 21 views
15

Z jakiegoś powodu jest przerwa po uruchomieniu poniższego programu. Uważam, że przyczyną jest WebClient().DownloadStringTaskAsync().Dlaczego blokuje WebClient.DownloadStringTaskAsync()? - nowy asynchroniczny interfejs API/składnia/CTP

class Program 
{ 
    static void Main(string[] args) 
    { 
     AsyncReturnTask(); 

     for (int i = 0; i < 15; i++) 
     { 
      Console.WriteLine(i); 
      Thread.Sleep(100); 
     } 
    } 

    public static async void AsyncReturnTask() 
    { 
     var result = await DownloadAndReturnTaskStringAsync(); 
     Console.WriteLine(result); 
    } 

    private static async Task<string> DownloadAndReturnTaskStringAsync() 
    { 
     return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov")); 
    } 
} 

O ile rozumiem, mój program powinien natychmiast rozpocząć liczenie od 0 do 15. czy robię coś źle?

Miałem ten sam problem z oryginalną próbką pobierania Netflix (którą otrzymałeś z CTP) - po naciśnięciu przycisku wyszukiwania interfejs użytkownika najpierw zawiesza się - i po pewnym czasie reaguje podczas ładowania kolejnych filmów. I wierzę, że to nie zamarzło w prezentacji Andersa Hejlsberga na PDC 2010.

Jeszcze jedno. Kiedy zamiast

return await new WebClient().DownloadStringTaskAsync(new Uri("http://www.weather.gov")); 

używam moje własne metody:

return await ReturnOrdinaryTask(); 

który jest:

public static Task<string> ReturnOrdinaryTask() 
{ 
    var t = Task.Factory.StartNew(() => 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      Console.WriteLine("------------- " + i.ToString()); 
      Thread.Sleep(100); 
     } 
     return "some text"; 
    }); 
    return t; 
} 

To działa tak jak powinno. Chodzi mi o to, że nic nie ładuje, ale zaczyna się od razu i nie blokuje głównego wątku, wykonując jego pracę.

Edit

OK, w co wierzę teraz to: funkcja WebClient.DownloadStringTaskAsync przykręcony jest do góry. Powinien działać bez początkowego okresu blokującego, tak:

static void Main(string[] args) 
    { 
     WebClient cli = new WebClient(); 
     Task.Factory.StartNew(() => 
      { 
       cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result); 
       cli.DownloadStringAsync(new Uri("http://www.weather.gov")); 
      }); 

     for (int i = 0; i < 100; i++) 
     { 
      Console.WriteLine(i); 
      Thread.Sleep(100); 
     } 
    } 

Odpowiedz

15

Choć program nie blokuje na pewien czas, to nie wznowić wykonanie w pętli for, zanim wynik jest zwracany z serwera zdalnego.

Należy pamiętać, że nowy interfejs asynchroniczny API jest nadal jednowątkowy. Tak więc WebClient().DownloadStringTaskAsync() nadal musi działać na twoim wątku, dopóki żądanie nie zostanie przygotowane i wysłane na serwer, zanim będzie możliwe await i powrót wydajności do przepływu twojego programu w Main().

Uważam, że wyniki, które widzisz, wynikają z faktu, że utworzenie i wysłanie żądania z komputera zabiera trochę czasu. Najpierw, gdy to się zakończy, implementacja DownloadStringTaskAsync może czekać na zakończenie operacji IO sieci i serwera zdalnego i może zwrócić ci wykonanie.

Z drugiej strony, twoja metoda RunOrdinaryTask tylko inicjuje zadanie i daje mu obciążenie pracą, i nakazuje mu rozpoczęcie. Potem natychmiast wraca. Dlatego nie widać opóźnienia podczas korzystania z RunOrdinaryTask.

Oto kilka linków na ten temat: Eric Lippert's blog (jeden z projektantów języków), a także o tym Jon Skeet's initial blog post. Eric ma serię 5 postów o stylu kontynuacji-przechodzenia, co tak naprawdę jest naprawdę ważne w przypadku async i await. Jeśli chcesz dokładnie poznać nową funkcję, możesz przeczytać posty Eric'a na temat CPS i Async. W każdym razie oba powyższe łącza dobrze ilustrują bardzo ważny fakt:

  • Asynchroniczny!= Równolegle

Innymi słowy, async i await nie wiruje nowe wątki dla Ciebie. Po prostu pozwalają wznowić wykonywanie normalnego przepływu, gdy wykonujesz operację blokowania - czas, w którym twój procesor po prostu usiądzie i nic nie zrobi w synchronicznym programie, czekając na zakończenie operacji zewnętrznej.

Edit

Wystarczy być jasne, co się dzieje: DownloadStringTaskAsync zakłada kontynuację, a następnie zwraca WebClient.DownloadStringAsync, w tym samym wątku, a następniedaje wykonanie z powrotem do swojego kodu. Dlatego czas blokowania, który widzisz przed rozpoczęciem liczenia pętli, jest czasem, który zajmuje ukończenie DownloadStringAsync. Twój program z asynchronizacją i oczekiwaniem jest bardzo zbliżony do następującego programu, który wykazuje takie samo zachowanie jak twój program: początkowy blok, a następnie zaczyna się odliczanie, a gdzieś w środku, asynchroniczne op kończy się i drukuje zawartość z wnioskowana URL:

static void Main(string[] args) 
    { 
     WebClient cli = new WebClient(); 
     cli.DownloadStringCompleted += (sender, e) => Console.WriteLine(e.Result); 
     cli.DownloadStringAsync(new Uri("http://www.weather.gov")); // Blocks until request has been prepared 

     for (int i = 0; i < 15; i++) 
     { 
      Console.WriteLine(i); 
      Thread.Sleep(100); 
     } 
    } 

Uwaga: jestem bynajmniej ekspertem w tej dziedzinie, więc mogę się mylić w niektórych punktach. Jeśli uważasz, że to źle, możesz poprawić moje zrozumienie tego tematu. Po prostu spojrzałem na prezentację PDC i wczoraj wieczorem pobawiłem się z CTP.

+0

tak - widzę, że działa tak, jak powiedziałeś - ale wierzę, że to jakiś błąd - chodzi o to, że operacje asynchroniczne, takie jak DownloadStringTaskAsync(), nie blokują wątku głównego/ui/wywołującego - wszystko, co wymaga czas powinien zaczynać się na innym wątku i natychmiast powrócić (w inny sposób - o co chodzi?). Tak więc pytanie brzmi: czy robię coś złego lub metoda DownloadStringTaskAsync() jest zepsuta - wiem, że jest to teraz protokół ctp/prototyp. Ale dziwne jest to, że wydawało się działać dobrze (bez wstępnego blokowania) na komputerze Hejlsberga podczas prezentacji. – agend

+0

Czy ktoś mógłby sprawdzić ten kod na swoim komputerze i potwierdzić początkowe zachowanie blokujące? TIA Arek – agend

+0

@agend, uruchomiłem to na mojej własnej maszynie i widzę początkowe zachowanie blokujące. – driis

2

Czy naciskasz klawisz F5 lub CTLR + F5, aby go uruchomić? W przypadku F5 opóźnienie dla VS polega tylko na wyszukaniu symboli AsyncCtpLibrary.dll ...

+0

f5 lub ctrl + f5 nie ma znaczenia - działa tak samo - opóźnienie wynosi około 5s, więc nie sądzę, że to symbole mają znaczenie. Mam takie samo zachowanie w wpf netflix_async_with_await przykład uzyskujesz z cts. – agend

+0

Wersja silverlight tego przykładu działa dobrze - początkowo nie zamarza - ale z jakiegoś powodu nie wyświetla ikon filmów – agend

+0

Po uruchomieniu projektu Silverlight lokalnie wyświetla tylko obrazy, jeśli jest to część projektu/strony ASP. –

5

Czy jesteś pewien, że problem nie jest związany z wykrywaniem ustawień proxy wykrytych w IE/Registry/Somewhere Slow?

Spróbuj ustawić webClient.Proxy = null (lub określając ustawienia w app.config), a okres "blokowania" powinien być minimalny.

+0

Miałem dokładnie ten problem –

Powiązane problemy