This question already has answers here:
WinForm Application UI Hangs during Long-Running Operation
(3 answers)
Closed 8 years ago.
I'm studying c# and tried other solutions that I've found on stackoverflow.. But I failed..
I'm trying to check if an URL exists when click a button.
When the button is clicked, a progressBar is set to marquee and the verification begins.
But the system stops until the result backs..
Here is the button click:
private void button1_Click(object sender, EventArgs e)
{
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
if (RemoteFileExists("http://www.gofdisodfi.com/"))
{
// OK
}
else
{
//FAIL
}
}
And here is the check:
private bool RemoteFileExists(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
return (response.StatusCode == HttpStatusCode.OK);
}
catch
{
return false;
}
}
You could use the "async and await" syntax for asynchronous action. It won't freeze the UI
private async void button1_Click(object sender, EventArgs e)
{
this.progressBar1.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
var result = await RemoteFileExists("http://www.google.com/");
if (result)
{
// OK
MessageBox.Show("OK");
}
else
{
//FAIL
MessageBox.Show("Fail");
}
}
private async Task<bool> RemoteFileExists(string url)
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
HttpWebResponse response = await request.GetResponseAsync() as HttpWebResponse;
return (response.StatusCode == HttpStatusCode.OK);
}
catch
{
return false;
}
}
You can read more about it here: http://blog.stephencleary.com/2012/02/async-and-await.html
How can I use HttpWebRequest (.NET, C#) asynchronously?
Use HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
The callback function is called when the asynchronous operation is complete. You need to at least call EndGetResponse() from this function.
By far the easiest way is by using TaskFactory.FromAsync from the TPL. It's literally a couple of lines of code when used in conjunction with the new async/await keywords:
var request = WebRequest.Create("http://www.stackoverflow.com");
var response = (HttpWebResponse) await Task.Factory
.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null);
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
If you can't use the C#5 compiler then the above can be accomplished using the Task.ContinueWith method:
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
Considering the answer:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
You could send the request pointer or any other object like this:
void StartWebRequest()
{
HttpWebRequest webRequest = ...;
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}
void FinishWebRequest(IAsyncResult result)
{
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Greetings
Everyone so far has been wrong, because BeginGetResponse() does some work on the current thread. From the documentation:
The BeginGetResponse method requires some synchronous setup tasks to
complete (DNS resolution, proxy detection, and TCP socket connection,
for example) before this method becomes asynchronous. As a result,
this method should never be called on a user interface (UI) thread
because it might take considerable time (up to several minutes
depending on network settings) to complete the initial synchronous
setup tasks before an exception for an error is thrown or the method
succeeds.
So to do this right:
void DoWithResponse(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
Action wrapperAction = () =>
{
request.BeginGetResponse(new AsyncCallback((iar) =>
{
var response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
responseAction(response);
}), request);
};
wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
{
var action = (Action)iar.AsyncState;
action.EndInvoke(iar);
}), wrapperAction);
}
You can then do what you need to with the response. For example:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
public static async Task<byte[]> GetBytesAsync(string url) {
var request = (HttpWebRequest)WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var content = new MemoryStream())
using (var responseStream = response.GetResponseStream()) {
await responseStream.CopyToAsync(content);
return content.ToArray();
}
}
public static async Task<string> GetStringAsync(string url) {
var bytes = await GetBytesAsync(url);
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
I ended up using BackgroundWorker, it is definitely asynchronous unlike some of the above solutions, it handles returning to the GUI thread for you, and it is very easy to understand.
It is also very easy to handle exceptions, as they end up in the RunWorkerCompleted method, but make sure you read this: Unhandled exceptions in BackgroundWorker
I used WebClient but obviously you could use HttpWebRequest.GetResponse if you wanted.
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) => {
args.Result = new WebClient().DownloadString(settings.test_url);
};
worker.RunWorkerCompleted += (sender, e) => {
if (e.Error != null) {
connectivityLabel.Text = "Error: " + e.Error.Message;
} else {
connectivityLabel.Text = "Connectivity OK";
Log.d("result:" + e.Result);
}
};
connectivityLabel.Text = "Testing Connectivity";
worker.RunWorkerAsync();
.NET has changed since many of these answers were posted, and I'd like to provide a more up-to-date answer. Use an async method to start a Task that will run on a background thread:
private async Task<String> MakeRequestAsync(String url)
{
String responseText = await Task.Run(() =>
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
WebResponse response = request.GetResponse();
Stream responseStream = response.GetResponseStream();
return new StreamReader(responseStream).ReadToEnd();
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
return null;
});
return responseText;
}
To use the async method:
String response = await MakeRequestAsync("http://example.com/");
Update:
This solution does not work for UWP apps which use WebRequest.GetResponseAsync() instead of WebRequest.GetResponse(), and it does not call the Dispose() methods where appropriate. #dragansr has a good alternative solution that addresses these issues.
public void GetResponseAsync (HttpWebRequest request, Action<HttpWebResponse> gotResponse)
{
if (request != null) {
request.BeginGetRequestStream ((r) => {
try { // there's a try/catch here because execution path is different from invokation one, exception here may cause a crash
HttpWebResponse response = request.EndGetResponse (r);
if (gotResponse != null)
gotResponse (response);
} catch (Exception x) {
Console.WriteLine ("Unable to get response for '" + request.RequestUri + "' Err: " + x);
}
}, null);
}
}
Follow up to the #Isak 's answer, which is very good. Nonetheless it's biggest flaw is that it will only call the responseAction if the response has status 200-299. The best way to fix this is:
private void DoWithResponseAsync(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
Action wrapperAction = () =>
{
request.BeginGetResponse(new AsyncCallback((iar) =>
{
HttpWebResponse response;
try
{
response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
}
catch (WebException ex)
{
// It needs to be done like this in order to read responses with error status:
response = ex.Response as HttpWebResponse;
}
responseAction(response);
}), request);
};
wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
{
var action = (Action)iar.AsyncState;
action.EndInvoke(iar);
}), wrapperAction);
}
And then as #Isak follows:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
I've been using this for async UWR, hopefully it helps someone
string uri = "http://some.place.online";
using (UnityWebRequest uwr = UnityWebRequest.Get(uri))
{
var asyncOp = uwr.SendWebRequest();
while (asyncOp.isDone == false) await Task.Delay(1000 / 30); // 30 hertz
if(uwr.result == UnityWebRequest.Result.Success) return uwr.downloadHandler.text;
Debug.LogError(uwr.error);
}
I need that Join method is called when Download.file method have finished.
I tried to add await keyword but it didn't work
Thread myThread = new Thread(new ThreadStart(()=> await Download.file(uri)));
Thread myThread = new Thread(new ThreadStart(()=>Download.file(uri)));
myThread.Start();
myThread.Join();
class Download{
public static async void file(string url)
{
try
{
HttpWebRequest request;
HttpWebResponse webResponse = null;
request = HttpWebRequest.CreateHttp(url);
request.AllowReadStreamBuffering = true;
webResponse = await request.GetResponseAsync() as HttpWebResponse;
Stream responseStream = webResponse.GetResponseStream();
using (StreamReader reader = new StreamReader(responseStream))
{
string content = await reader.ReadToEndAsync();
}
webResponse.Close();
}
catch (Exception ex) {
Debug.WriteLine(ex.Message);
}
}
}
Thanks
You should make your file method (which is badly named, by the way - it should probably be something like DownloadFileAsync) return Task instead of void.
Then you can await it.
However, it's not clear why you're starting this in a different thread anyway - the point of asynchrony is that you don't need to start a new thread. From another async method, you can just use:
await Download.file(uri);
(Of course the fact that the method isn't doing anything with the content is a little strange...)
You should also consider using HttpClient or WebClient, both of which have this behaviour already available.
I have this app which authenticates a user with external web service and should navigate to a different view once authenticated.
The authentication is done in a HttpWebRequest:
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest request = (HttpWebRequest)System.Net.WebRequest.Create("http://webservice");
request.Method = "GET";
request.BeginGetResponse(new AsyncCallback(CheckLogin), request);
}
Then here is the callback:
private void CheckLogin(IAsyncResult asynchronousResult)
{
HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;
bool success = false;
try
{
// End the operation
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);
Stream streamResponse = response.GetResponseStream();
StreamReader streamRead = new StreamReader(streamResponse);
string responseString = streamRead.ReadToEnd();
if (responseString.Contains("ok"))
{
success = true;
}
streamResponse.Dispose();
streamRead.Dispose();
response.Dispose();
}
catch (Exception e)
{
}
request.Abort();
request = null;
if (success)
{
this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
Frame.Navigate(typeof(Main2));
}).AsTask().Wait();
}
}
This is working perfectly when debugging in Visual Studio but when I have published the application and installed the package, it hangs on Frame.Navigate. I guess this is because the CheckLogin method is not running in the UI thread.
Any ideas on how to Frame.Navigate(..) in a background thread?
It seems to be no real problems between Dispatcher.RunAsync and Frame.Navigate. So I assume you're not looking in the right direction.
Maybe your page's content is faulty, instead of the navigation.
Hi guys just wondering if somebody could help me try and correctly thread my application, I am constantly hitting an hurdle after another, I have never been to clued up on threading in applications. I have tryed following this http://www.developerfusion.com/code/4654/asynchronous-httpwebrequest/ tutorial.
basically I'm just trying to stop my request from hanging my application
public class Twitter
{
private const string _username = "****",
_password = "****";
private WebResponse webResp;
public string getTimeLine()
{
Thread thread = new Thread(new ThreadStart(TwitterRequestTimeLine));
thread.IsBackground = true;
thread.Start();
using (Stream responseStream = webResp.GetResponseStream())
{
//
using (StreamReader reader = new StreamReader(responseStream))
{
return reader.ReadToEnd();
}
}
}
private void TwitterRequestTimeLine()
{
string aUrl = "http://168.143.162.116/statuses/home_timeline.xml";
HttpWebRequest request = (HttpWebRequest) HttpWebRequest.Create(aUrl);
SetRequestParams(request);
request.Credentials = new NetworkCredential(_username, _password);
//WebResponse tempResp = request.GetResponse();
ThreadState state = new ThreadState();
IAsyncResult result = request.BeginGetResponse(new AsyncCallback(???), ???);
}
private static void SetRequestParams( HttpWebRequest request )
{
request.Timeout = 500000;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.UserAgent = "AdverTwitment";
}
}
}
anyone help would be greatly appricated
You really don't need to thread HttpWebRequest.
When you use BeginGetResponse() and EndGetResponse() with HttpWebRequest, it already uses a background thread for you in order to work asynchronously. There is no reason to push this into a background thread.
As for usage: The help for HttpWebRequest.BeginGetResponse demonstrates a complete, asynchronous request.
If this is a WinForms app, the easiest way to keep the GUI responsive while executing the WebRequest is to use a BackgroundWorker component. Drop a BackgroundWorker on your form and call its RunWorkAsync() method. Put the code to execute the WebRequest and read the Response in the DoWork event handler.
Try using an AsyncCallback like Rubens suggested but have the callback call into a separate method to load the data to its destination. If the getTimeline method doesn't return immediately it will cause the application to hang, because the UI Thread is what is running the request itself.
If you use a separate AsyncCallback to be called after the request is done and have it load the data then the method will return immediately and your UI thread can do other things while it waits.
What about this:
private string getTimeLine()
{
string responseValue = "";
string aUrl = "http://168.143.162.116/statuses/home_timeline.xml";
AutoResetEvent syncRequest = new AutoResetEvent(false);
WebRequest request = WebRequest.Create(aUrl);
request.Method = "POST";
request.BeginGetResponse(getResponseResult =>
{
HttpWebResponse response =
(HttpWebResponse)request.EndGetResponse(getResponseResult);
using (StreamReader reader =
new StreamReader(response.GetResponseStream()))
{
responseValue = reader.ReadToEnd();
}
syncRequest.Set();
}, null);
syncRequest.WaitOne();
return responseValue;
}
EDIT: Ok, I tried to keep a method returning a string, that's why I used AutoResetEvent; If you use a BackgroundWorker, you'll get notified when your data is available:
BackgroundWorker worker = new BackgroundWorker();
string responseValue = "";
worker.RunWorkerCompleted += (sender, e) =>
{
// update interface using responseValue variable
};
worker.DoWork += (sender, e) =>
{
string aUrl = "http://168.143.162.116/statuses/home_timeline.xml";
WebRequest request = WebRequest.Create(aUrl);
// .. setup
using(StreamReader reader =
new StreamReader(request.GetResponse().GetResponseStream()))
responseValue = reader.ReadToEnd();
};
worker.RunWorkerAsync();