【问题标题】:ManualResetEvent.WaitOne blocks all threadsManualResetEvent.WaitOne 阻塞所有线程
【发布时间】:2012-03-10 16:08:56
【问题描述】:

我有以下代码

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() 不应该阻止他。

我需要的是创建一个同步方法而不是异步。

谢谢!

UPD:使用异步调用更新源代码。

【问题讨论】:

  • 您的downloadHandle.WaitOne 代码是从哪个线程调用的? UI 线程?

标签: multithreading silverlight windows-phone-7 webclient manualresetevent


【解决方案1】:

我的猜测:您正在从 UI 线程调用 downloadHandle.WaitOne()。如果您正在执行来自 UI 事件处理程序的代码(例如,单击按钮或导航到新页面)或事件处理程序调用的函数,那么您就在 UI 线程中。

为什么会出现这个问题?

果然DownloadAsync在后台执行,多亏了线程池。但是,WebClient 类总是使用 UI 线程执行它的回调(即您的 client_DownloadStringCompleted 方法)......但是这个相同的线程被您的 downloadHandle.WaitOne() 阻塞了!

这就是为什么,当您对锁设置超时时,client_DownloadStringCompleted 方法会神奇地执行。

如何解决这个问题?两种解决方案。

1/ 停止在主线程中执行downloadHandle.WaitOne()。它会阻塞用户界面,并且您的应用程序变得无响应,这绝不是一件好事。

2/ 使用HttpWebRequest 而不是WebClientHttpWebRequest 在开始下载的同一个线程上执行回调,所以你不会遇到这个死锁问题。

【讨论】:

    【解决方案2】:

    当你从UI 调用downloadHandle.WaitOne(); 时,你会阻止UI thread,所以ThreadPool 永远不会调用。移至背景:

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

    【讨论】:

    • 移到后台是什么意思?这段代码在 ViewModel 中处理,所以 UI 线程没有被阻塞。
    • 如果您从UI 调用ViewModel 方法,它将在UI 上执行。但我不知道你打电话的背景,所以我只是猜测问题出在哪里。
    【解决方案3】:

    可能存在阻止调用完成回调方法的异常。你检查过它是否被调用了吗?

    顺便说一句,你实际上不需要在这里使用线程池——你可以在你的主线程上调用 DownloadAsync(),因为它不会阻塞。

    【讨论】:

      【解决方案4】:

      client.DownloadString 是同步方法,所以你完成的处理程序永远不会被调用。需要调用异步版本:client.DownloadStringAsync()

      您可以在 msdn 上阅读有关 DownloadStringAsync 的更多信息。如果您依赖于应该调用某些代码的事实,那么将代码放入 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();
      }
      

      【讨论】:

      • ...当然还有这个 :-) -- 但也请注意,您不必使用此处的线程池来调用 DownloadAsync。
      • @SashaGoldshtein 当然你是对的,我之前没有发现这一点。 +1
      • 无论如何它都不起作用 =) 在未设置 downloadHandle 期间永远不会调用 client_DownloadStringCompleted。
      • @RomanGolenok - 您是否直接调用了 DownloadAsync,并且您确定在调用 DownloadStringAsync 之前没有异常,例如通过捕获异常或尝试在 DownloadStringAsync() 之后记录某些内容?
      • 我确定。调用了 client.DownloadStringAsync,但没有调用事件处理程序。因此,如果我将 ManualResetEvent 替换为 AutoResetEvent,持续时间为 3 秒,则在这 3 秒之后将调用 client_DownloadStringCompleted,但也会调用 downloadHandle.WaitOne,此时结果将为空。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-16
      • 1970-01-01
      • 2015-12-04
      相关资源
      最近更新 更多