/ / ManualResetEvent.WaitOne blokuje wszystkie wątki - wielowątkowość, silverlight, windows-phone-7, webclient, manualresetevent

ManualResetEvent.WaitOne blokuje wszystkie wątki - wielowątkowość, silverlight, windows-phone-7, webclient, manualresetevent

Mam następujący kod

ThreadPool.QueueUserWorkItem(new WaitCallback(DownloadAsync), apiMethod);
downloadHandle.WaitOne();

Gdzie jest DownloadAsync

private void DownloadAsync(object _uri)
{
var url = _uri as string;
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri(GLOBALS.MAIN_API_URL + url, UriKind.Absolute));
}

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
result = e.Result;
downloadHandle.Set();
}

Więc moim problemem jest to, że downloadHandle.Set () nigdy nie będzie wywoływany. Ale nie rozumiem dlaczego? Tworzę nowy wątek dla DownloadAsync i downloadHandle.WaitOne () nie powinien go blokować.

Potrzebuję stworzyć metodę synchronizacji zamiast Async.

Dzięki!

UPD: Zaktualizowano kod źródłowy przy wywołaniu Async.

Odpowiedzi:

4 dla odpowiedzi № 1

client.DownloadString jest metodą synchroniczną, więc Twój kompletny moduł obsługi nigdy nie zostanie wywołany. Musisz wywołać wersję asynchroniczną: client.DownloadStringAsync()

Możesz przeczytać więcej na temat DownloadStringAsync na msdn. Mądrze jest również umieszczać kod w bloku try-catch i obsługiwać wyjątki, jeśli polegasz na tym, że jakiś kod powinien zostać wywołany.

Twój kod może wyglądać tak:

private void DownloadAsync(object _uri)
{
try
{
var url = _uri as string;
WebClient client = new WebClient();
client.DownloadStringCompleted += new DownloadStringCompletedEventHandler(client_DownloadStringCompleted);
client.DownloadStringAsync(new Uri(GLOBALS.MAIN_API_URL + url, UriKind.Absolute));
}
catch //appropriate exception
{
//Handle exception (maybe set downloadHandle or report an error)
}
}

void client_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
result = e.Result;
downloadHandle.Set();
}

2 dla odpowiedzi nr 2

Może istnieć wyjątek, który uniemożliwia wywołanie metody wywołania zakończenia. Czy sprawdziłeś, czy w ogóle został wywołany?

Nawiasem mówiąc, tak naprawdę nie musisz tutaj używać puli wątków - możesz wywołać DownloadAsync () w głównym wątku, ponieważ się nie blokuje.


2 dla odpowiedzi nr 3

Domyślam się, że dzwonisz downloadHandle.WaitOne() z wątku interfejsu użytkownika. Jeśli wykonujesz kod z modułu obsługi zdarzeń interfejsu użytkownika (na przykład kliknięcie przycisku lub nawigacja do nowej strony) lub funkcji wywoływanej przez moduł obsługi zdarzeń, oznacza to, że znajdujesz się w wątku interfejsu użytkownika.

Dlaczego to jest problem?

Jasne, DownloadAsync jest wykonywany w tle dzięki puli wątków. Jednak klasa WebClient zawsze wykonuje swoje wywołanie zwrotne (to znaczy twoja client_DownloadStringCompleted metoda) za pomocą wątku interfejsu użytkownika ... Ale ten sam wątek jest blokowany przez użytkownika downloadHandle.WaitOne()!

Właśnie dlatego, gdy ustawisz limit czasu na zamku, client_DownloadStringCompleted metoda zostaje magicznie wykonana.

Jak to naprawić? Dwa rozwiązania.

1 / Zatrzymaj wykonywanie downloadHandle.WaitOne() w głównym wątku. Blokuje interfejs użytkownika, a twoja aplikacja przestaje odpowiadać nigdy dobra rzecz.

2 / Użyj HttpWebRequest zamiast WebClient. HttpWebRequest wykonuje wywołanie zwrotne w tym samym wątku, który rozpoczął pobieranie, więc nie masz problemu z zakleszczeniem.


0 dla odpowiedzi nr 4

Kiedy zadzwonisz downloadHandle.WaitOne(); od UI blokujesz UI thread, więc ThreadPool nigdy nie zadzwonię. Przejdź do tła:

ThreadPool.QueueUserWorkItem(new WaitCallback(DownloadAsync), apiMethod);
downloadHandle.WaitOne();