2013-07-12 4 views
0

Я пытаюсь использовать объект AutoResetEvent для блокировки потока до асинхронизации. загрузка WebClient выполняется.Ошибка использования AutoResetEvent

Моя проблема в том, что когда я вызываю WaitOne(), поток просто блокируется там, и VS никогда не достигает точки останова в методе обработчика события DownloadComplete.

Вот мой код

//Class used to pass arguments to WebClient's events... 

public class RunArgs 
{ 
    public JobInfo jobInfo; 
    public int jobTotal; 
    public int jobIndex; 
    public AutoResetEvent AutoResetEventObject; 
} 

List<JobInfo> jl = ConfigSectionWrapper.GetAllJobs(); 

int jobAmount = jl.Count; 
int jobIndex = 0; 

RunArgs args = new RunArgs(); 
args.jobTotal = jl.Count; 


foreach (JobInfo ji in jl) 
{ 


    if (ji.enabled == "0") 
    { 
     args.jobIndex++; 
     continue; 
    } 

    try 
    { 
     args.jobIndex++; 
     args.jobInfo = ji; 

     appLog.Source = ji.eventSource; 
     appLog.WriteEntry(string.Format("Started job {0}...", ji.jobName),   EventLogEntryType.Information); 
     ji.fullFileName = string.Format(ji.reportFileName, string.Format("{0}-{1}-{2}", DateTime.Now.Year.ToString(), DateTime.Now.Month.ToString().PadLeft(2, '0'), DateTime.Now.Day.ToString().PadLeft(2, '0'))); 
     ji.fullFileName = string.Format("{0}{1}", ji.downloadDirectory, ji.fullFileName); 

     using (WebClient wc = new WebClient()) 
     { 
      AutoResetEvent notifier = new AutoResetEvent(false); 
      args.AutoResetEventObject = notifier; 
      wc.Credentials = CredentialCache.DefaultNetworkCredentials; 
      wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted); 
      wc.DownloadFileAsync(new Uri(args.jobInfo.reportURL), args.jobInfo.fullFileName, args); //Pass the args params to event handler... 
      notifier.WaitOne(); 
     } 
    } 
    catch (Exception ex) 
    { 
     appLog.WriteEntry(string.Format("Error starting report execution: {0}", ex.Message), EventLogEntryType.Error); 
     DeleteFile(ji.fullFileName); 
    } 

} 

private void DownloadCompleted(object sender, AsyncCompletedEventArgs e) 
{ 

    RunArgs args = (RunArgs)e.UserState; 

    //Do things.... 

    args.AutoResetEventObject.Set(); 
} 

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

Все, что явно не так?

ответ

0

Вот что я в конечном итоге делает:

private AutoResetEvent notifier = new AutoResetEvent(false); 

Теперь главный цикл выглядит следующим образом:

 foreach (JobInfo ji in jl) 
     { 
      if (ji.enabled == "0") 
      { 
       args.jobIndex++; 
       continue; 
      } 

      args.jobInfo = ji; 
      Thread t = new Thread(new ParameterizedThreadStart(startDownload)); 
      t.Start(args); 
      notifier.WaitOne(); 
     } 

private void startDownload(object startArgs) 
    { 
     RunArgs args = (RunArgs)startArgs; 

     try 
     { 
      args.jobIndex++; 
      appLog.Source = args.jobInfo.eventSource; 
      appLog.WriteEntry(string.Format("Started job {0}...", args.jobInfo.jobName), EventLogEntryType.Information); 
      args.jobInfo.fullFileName = string.Format(args.jobInfo.reportFileName, string.Format("{0}-{1}-{2}", DateTime.Now.Year.ToString(), DateTime.Now.Month.ToString().PadLeft(2, '0'), DateTime.Now.Day.ToString().PadLeft(2, '0'))); 
      args.jobInfo.fullFileName = string.Format("{0}{1}", args.jobInfo.downloadDirectory, args.jobInfo.fullFileName); 

      WebClient wc = new WebClient(); 

      wc.Credentials = CredentialCache.DefaultNetworkCredentials; 
      wc.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted); 
      wc.DownloadFileAsync(new Uri(args.jobInfo.reportURL), args.jobInfo.fullFileName, args); //Pass the args params to event handler... 

     } 
     catch (Exception ex) 
     { 
      appLog.WriteEntry(string.Format("Error starting report execution: {0}", ex.Message), EventLogEntryType.Error); 
      DeleteFile(args.jobInfo.fullFileName); 
      notifier.Set(); 
     } 

    } 

Так что теперь AutoResetEvent блокирует основной поток, но т может успешно вызвать DownloadFileCompleteEvent.И в событии DownloadFileCompleted я, очевидно, делаю notifier.Set() тоже.

Спасибо всем!

1

Я не нашел документов для поддержки этого, но, глядя на код WebClient в Reflector, кажется, что события поднимаются в основном потоке, а не в фоновом потоке. Поскольку ваш основной поток блокируется, когда вы вызываете notifier.WaitOne(), обработчик события никогда не вызывается.

Если приведенный вами пример точно представляет ваш код, нет необходимости использовать wc.DownloadFileAsync() вместо wc.DownloadFile(). То, что вызов notifier.WaitOne() в конечном итоге превращает его в синхронную операцию. Если вы ищете истинную асинхронную операцию, вам придется делать это по-другому.

+0

Да, это старый проект, который я работал несколько месяцев назад, и я думаю, что была причина, по которой я не использовал DownloadFile() (sync). Я думаю, что таймаут на нем не может быть расширен до моих потребностей (я загружаю отчеты SSRS, которые могут занять 5-10 минут) –

+0

Вам нужно будет переписать это. Вам нужно будет хранить экземпляр «WebClient» (удалите оператор 'using') и вызовите' Dispose' на нем позже (например, когда вызывается обработчик событий). И ожидаем события на главной теме. –

+0

Итак, Брайан, тогда вы не видите ничего плохого в коде, который я разместил? –

2

WebClient использует AsyncOperationManager для управления асинхронными операциями. Поэтому убедитесь, что вы знаете, как эти асинхронные операции вызывают в разных сценариях.

Под WinForms

Под WinForm приложения AsyncOperationManager использует WindowsFormsSynchronizationContext. Как говорит @Michael, вызов WaitOne() блокирует поток основной формы при запуске события DownloadCompleted. В WinFormsDownloadCompleted выполнен на основной линии WinForm.

Итак, удалите notifier.WaitOne(), и он должен работать. DownloadCompleted должен быть вызван потоком главного окна (предположительно, тот, который вы блокируете WaitOne()).

Под консольные приложения

В соответствии с приложением Console введите AsyncOperationManager использует System.Threading.SynchronizationContext и DownloadCompleted выполняется асинхронно с помощью нити образуют ThreadPool.

Нет проблемы с звонком notifier.WaitOne() под кодом Console; и приведенный выше код работает так, как ожидалось.

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