TL;DR: Your web request handler will execute on a different thread. Ensure you don't do anything that isn't thread-safe in that handler. You can use Invoke
to dispatch your callback handler's code to the main thread.
Diagnosis
The problem here is almost certainly hiding in the missing details of your asynchronous call.
// here I call a remote webservice asynchronously.
Asynchronously is a little bit too vague to be sure what exactly is happening, but there's a very good chance that the asynchronous mechanism that you are using has executed its callback on a different thread from the main UI thread.
Overview
This is common in the .NET model. Asynchronous I/O in the .NET model makes use of threads in a thread pool to handle I/O via I/O Completion Ports (IOCP). It means that when a call like Socket.BeginReceive
or WebRequest.BeginGetResponse
(or any .NET asynchronous web request that uses similar technology internally) completes, the callback will execute on a thread in the thread pool, not the main thread. This may be surprising to you, since you didn't actively create another thread; you just participated in making asynchronous calls.
You must be very careful about what you do in the callback from your web request as many user-interface / Windows Forms operations are not permitted on any thread other than the main UI thread. Similarly, it may not be the UI itself that is causing you problems, you may have just accessed some resource or object that is not thread safe. Many seemingly innocuous things can cause a crash or exception if you're not careful with multithreading.
To resolve the issue:
If in doubt, in your callback, as early as you can, dispatch (a.k.a. Invoke
) the code in your handler so that it runs on the main thread.
A common pattern for doing this would be something like what follows below.
Suppose you have made a call like this:
IAsyncResult result = (IAsyncResult myHttpWebRequest.BeginGetResponse(
new AsyncCallback(RespoCallback), myRequestState);
The handler might be set up like this:
private static void RespCallback(IAsyncResult asynchronousResult)
{
// THIS IS NOT GOING TO WORK BECAUSE WE ARE ON THE WRONG THREAD. e.g.:
this.label1.Text = "OK"; // BOOM! :(
}
Instead, dispatch any necessary processing back to the main thread.
private static void RespCallback(IAsyncResult asynchronousResult)
{
this.Invoke((MethodInvoker) delegate {
// This block of code will run on the main thread.
// It is safe to do UI things now. e.g.:
this.label1.Text = "OK"; // HOORAY! :)
});
}
I'm not advising this as a general best practice. I'm not saying to just immediately dispatch all your handlers back to the main thread. One size does not fit all. You should really look at the specific details of what you do in your handler and ensure you aren't doing thread-specific things. But I am saying that in the absence of any kind of explanation from you about what your asynchronous handlers are doing, the problem would likely be solved by invoking the handler code on the main thread.
Note: Of course, to fix your problem with this technique, it requires that your main thread is running. If you blocked your main thread with a (bad) technique like the one in this example then you'll have to redesign part of your app. Here's an example of something that would require a bigger rework:
// Start the asynchronous request.
IAsyncResult result=
(IAsyncResult) myHttpWebRequest.BeginGetResponse(new AsyncCallback(RespCallback),myRequestState);
// this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
ThreadPool.RegisterWaitForSingleObject (result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), myHttpWebRequest, DefaultTimeout, true);
// The response came in the allowed time. The work processing will happen in the
// callback function.
allDone.WaitOne(); // *** DANGER: This blocks the main thread, the IO thread
// won't be able to dispatch any work to it via `invoke`
Notice the WaitOne
call? That blocks execution of the executing thread. If this code executes on the main thread, then the main thread will be blocked until the WebRequest completes. You'll have to redesign so that either you don't block the main thread (my recommendation) or that you more closely examine your callback handler to see why what it's doing is conflicting with other threads.
Main
method have the[STAThread]
attribute. Seems like a threading issue. – Starvation