ManualResetEvent.WaitOne блокирует все потоки - PullRequest
3 голосов
/ 20 февраля 2012

У меня следующий код

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 () не должен блокировать его.

Мне нужно создать метод Sync вместо Async.

Спасибо!

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

Ответы [ 4 ]

4 голосов
/ 20 февраля 2012

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();
}
2 голосов
/ 20 февраля 2012

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

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

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

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

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

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

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

2 голосов
/ 20 февраля 2012

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

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

0 голосов
/ 20 февраля 2012

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

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