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

我需要的是创建一个Sync方法,而不是Async。

谢谢!

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

+0

从哪个线程调用您的'downloadHandle.WaitOne'代码? UI线程? – 2012-02-20 10:58:14

回答

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

...当然还有这个:-) - 但也要注意,你不必在这里使用线程池来调用DownloadAsync。 – 2012-02-20 09:52:28

+0

@SashaGoldshtein当然你是对的,我以前没有发现过。 +1 – 2012-02-20 09:57:54

+0

它无法正常工作=)client_DownloadStringCompleted将永远不会调用downloadHandle没有设置。 – 2012-02-20 10:17:36

2

有可能是防止被调用的完成回调方法的例外。你有没有检查它是否被调用?

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

0

当您拨打downloadHandle.WaitOne();UI您块UI thread,所以ThreadPool将永远不会被调用。移动到背景:

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

你是什么意思移动到背景?此代码在ViewModel中处理,因此UI线程未被阻止。 – 2012-02-20 10:20:29

+0

如果您从'UI'调用'ViewModel'方法,它将在'UI'上执行。但是我不知道你的电话的内容,所以我只是猜猜问题出在哪里。 – Ku6opr 2012-02-20 10:41:02

2

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

为什么这是一个问题?

果然,DownloadAsync在后台执行,这要归功于线程池。但是,WebClient类总是使用UI线程执行其回调(即您的client_DownloadStringCompleted方法)...但是,这个相同的线程被您的downloadHandle.WaitOne()阻止!

这就是为什么当你把锁定超时时,client_DownloadStringCompleted方法会被神奇地执行。

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

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

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