Reputation: 1417
i have following code
ThreadPool.QueueUserWorkItem(new WaitCallback(DownloadAsync), apiMethod);
downloadHandle.WaitOne();
Where DownloadAsync is
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();
}
So my problem is that downloadHandle.Set() will never called. But i don't understand why? I Create a new thread for DownloadAsync and downloadHandle.WaitOne() shouldn't block him.
That i need is create a Sync method instead of Async.
Thanks!
UPD: Updated source code with Async calling.
Upvotes: 3
Views: 2849
Reputation: 39007
My guess: you're calling downloadHandle.WaitOne()
from the UI thread. If you're executing code from an UI event handler (for instance, a click to a button, or a navigation to a new page), or a function called by the event handler, then you're in the UI thread.
Why is this a problem?
Sure enough, DownloadAsync
is executed in the background, thanks to the threadpool. However, the WebClient class always executes its callback (that is, your client_DownloadStringCompleted
method) using the UI thread... But this very same thread is blocked by your downloadHandle.WaitOne()
!
That's why, when you put a timeout on your lock, the client_DownloadStringCompleted
method gets magically executed.
How to fix this? Two solutions.
1/ Stop executing downloadHandle.WaitOne()
in the main thread. It blocks the user interface and your application becomes unresponsive, which is never a good thing.
2/ Use HttpWebRequest
instead of WebClient
. HttpWebRequest
executes the callback on the same thread that started the download, so you won't have this deadlock issue.
Upvotes: 2
Reputation: 8126
When you call downloadHandle.WaitOne();
from UI
you block UI thread
, so ThreadPool
will never called. Move to background this:
ThreadPool.QueueUserWorkItem(new WaitCallback(DownloadAsync), apiMethod);
downloadHandle.WaitOne();
Upvotes: 0
Reputation: 3519
There might be an exception that prevents the completion callback method from being called. Did you check whether it was called at all?
By the way, you don't actually need to use the thread pool here -- you can call DownloadAsync() on your main thread, because it does not block.
Upvotes: 2
Reputation: 11957
client.DownloadString
is synchronous method, so your completed handler will never be called. You need to call asynchronous version: client.DownloadStringAsync()
You can read more about DownloadStringAsync on msdn. It's also wise to put code in try-catch block and handle exceptions if you are relying on the fact, that some code should be called.
Your code could look like this:
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();
}
Upvotes: 4