3

я следующий кодManualResetEvent.WaitOne блокирует все нити

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

Где 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(); 
     } 

Так что моя проблема в том, что downloadHandle.Set() никогда не вызывается. Но я не понимаю, почему? Я создаю новый поток для DownloadAsync и downloadHandle.WaitOne() не должен его блокировать.

Мне нужно создать метод синхронизации вместо Async.

Спасибо!

UPD: Обновлен исходный код с вызовом Async.

+0

От какого потока вызывается ваш код 'downloadHandle.WaitOne'? Пользовательский интерфейс? –

ответ

4

client.DownloadString является синхронным методом, поэтому ваш обработчик никогда не будет вызван. Вы должны назвать асинхронную версию: client.DownloadStringAsync()

Вы можете узнать больше о DownloadStringAsync на msdn. Также разумно помещать код в блок try-catch и обрабатывать исключения, если вы полагаетесь на факт, что нужно вызвать некоторый код.

Ваш код может выглядеть следующим образом:

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(); 
} 
+0

... и, конечно же, есть это :-) - но также обратите внимание, что вам не нужно использовать пул потоков здесь, чтобы вызвать DownloadAsync. –

+0

@SashaGoldshtein Конечно, вы правы, я не заметил этого раньше. +1 –

+0

Это не работает в любом случае =) client_DownloadStringCompleted никогда не вызывается во время downloadHandle не установлен. –

2

Там может быть исключением, что предотвращает завершение метода обратного вызова от вызова. Вы проверяли, было ли это вообще вызвано?

Кстати, вам фактически не нужно использовать пул потоков здесь - вы можете вызвать DownloadAsync() в своем основном потоке, потому что он не блокируется.

0

Когда вы звоните downloadHandle.WaitOne(); от UI, вы блокируете UI thread, поэтому ThreadPool никогда не будет звонить. Переместить в фоне этого:

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

Что значит перемещение на задний план? Этот код обрабатывается в ViewModel, поэтому поток пользовательского интерфейса не блокируется. –

+0

Если вы вызываете метод 'ViewModel' из' UI', он будет выполнен на 'UI'. Но я не знаю контекста вашего вызова, поэтому я просто думаю, где была проблема. – Ku6opr

2

Моя догадка: вы звоните downloadHandle.WaitOne() из потока пользовательского интерфейса. Если вы выполняете код из обработчика событий пользовательского интерфейса (например, нажатие на кнопку или переключение на новую страницу) или функцию, вызванную обработчиком событий, то вы находитесь в потоке пользовательского интерфейса.

Почему это проблема?

Достаточно, DownloadAsync выполнен в фоновом режиме, благодаря threadpool. Тем не менее, класс WebClient всегда выполняет свой обратный вызов (то есть ваш метод client_DownloadStringCompleted) с использованием потока пользовательского интерфейса ... Но этот же поток блокируется вашим downloadHandle.WaitOne()!

Именно поэтому, когда вы накладываете таймаут на свой замок, метод client_DownloadStringCompleted получает магический кадр.

Как это исправить? Два решения.

1/Прекратить выполнение downloadHandle.WaitOne() в основной теме. Он блокирует пользовательский интерфейс, и ваше приложение перестает отвечать на запросы, то есть никогда хорошая вещь.

2/Использовать HttpWebRequest вместо WebClient.HttpWebRequest выполняет обратный вызов в том же потоке, который начал загрузку, поэтому у вас не будет этой проблемы с блокировкой.

Смежные вопросы