WebRequest hangs user interface - c#

I'm doing simple request with WebRequest and the app just hangs until the response comes back. How do I fix this?
I have read a lot of topics and they all say to use threads. I don't know how to use them; can anyone provide an example of the following that doesn't hang the user interface?
private string SimpleRequest(String url)
{
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
string result = reader.ReadToEnd();
stream.Dispose();
reader.Dispose();
return result;
}
private void button1_Click(object sender, EventArgs e)
{
String googleHtml = simpleRequest("https://facebook.com");
}
Thanks!

If you are using c# 5.0 it is too easy
public async void Test1()
{
WebClient wc = new WebClient();
richTextBox1.Text = await wc.DownloadStringTaskAsync("https://facebook.com");
}
You can convert your method to awaitable also
private Task<string> simpleRequest(String url)
{
return Task.Factory.StartNew(() =>
{
WebRequest request = WebRequest.Create(url);
WebResponse response = request.GetResponse();
Stream stream = response.GetResponseStream();
StreamReader reader = new StreamReader(stream);
string result = reader.ReadToEnd();
stream.Dispose();
reader.Dispose();
return result;
});
}
and call as
public async void Test2()
{
richTextBox1.Text = await simpleRequest("https://facebook.com");
}
For lower versions of C#, See other answers.

You can use a BackgroundWorker in order to use another thread than UI thread. Here is a sample.
You can also cancel or display progress:
public partial class fmMain : Form
{
private void btnrunBackgroundWorker_Click( object sender, EventArgs e )
{
// Create a background worker
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler( bw_DoWork );
// run in another thread
bw.RunWorkerAsync();
}
private void bw_DoWork( object sender, DoWorkEventArgs e )
{
String facebookHtml = simpleRequest("https://facebook.com");
}
}

You're doing network access on the main UI thread. The UI has a main thread which loops and updates the UI. If you do network access on that thread, the UI will not update and it will appear hung until your method returns and the loop can continue.
Consider doing your work on a background thread. One easy way to do that in .net is the BackgroundWorker class.
The official documentation has a nice example of it's usage:
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
The BackgroundWorker class allows you to run an operation on a
separate, dedicated thread. Time-consuming operations like downloads
and database transactions can cause your user interface (UI) to seem
as though it has stopped responding while they are running. When you
want a responsive UI and you are faced with long delays associated
with such operations, the BackgroundWorker class provides a convenient
solution.

You should use a BackgroundWorker so your main UI thread does not hang!
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync("https://facebook.com"); // start async operation
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = simpleRequest(e.Argument.ToString()); // set result in async operation
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error == null)
{
// show result here
// or access/manage result here
MessageBox.Show(e.Result.ToString());
}
else
{
// manage/show error
MessageBox.Show(e.Error.Message);
}
}
private String simpleRequest(String url)
{
// your code goes here
}
You could implement backgroundWorker1_ProgressChanged too, so that the user can see progress of loading webSite. (sideNote: see this question for details on ProgressChanged and Context)

Related

System.Net.Http.HttpClient.PostAsync blocks and never returns

I have a .NET framework Windows Forms application with a form that has this code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace test
{
public partial class Main : Form
{
public int exitCode = 1;
private Options opts;
CancellationTokenSource cancellationSource = new CancellationTokenSource();
public Main(Options opts)
{
InitializeComponent();
this.opts = opts;
}
private void btnCancel_Click(object sender, EventArgs e)
{
exitCode = 1;
cancellationSource.Cancel();
Close();
}
async Task doUpload()
{
using (var content = new MultipartFormDataContent())
{
List<FileStream> streams = new List<FileStream>();
try
{
foreach (string fPath in opts.InputFiles)
{
FileStream stream = new FileStream(fPath, FileMode.Open, FileAccess.Read);
streams.Add(stream);
content.Add(new StreamContent(stream), fPath);
}
var progressContent = new ProgressableStreamContent(
content,
4096,
(sent, total) =>
{
double percent = 100 * sent / total;
progressBar.Value = (int)percent;
});
using (var client = new HttpClient())
{
using (var response = await client.PostAsync(opts.URL, progressContent, cancellationSource.Token))
{
if (response.IsSuccessStatusCode)
{
exitCode = 0;
}
else
{
MessageBox.Show(
response.Content.ToString(),
"Error " + response.StatusCode,
MessageBoxButtons.OK, MessageBoxIcon.Error
);
}
Close();
}
}
}
finally
{
foreach (FileStream stream in streams)
{
stream.Close();
}
}
}
}
private void Main_Load(object sender, EventArgs e)
{
}
private void Main_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = !cancellationSource.IsCancellationRequested;
}
private void Main_Shown(object sender, EventArgs e)
{
doUpload();
}
}
}
The ProgressableStreamContent is the same that was given here: C#: HttpClient, File upload progress when uploading multiple file as MultipartFormDataContent
The problem is that the response is never returned. In other words: await for postAsync never completes. Also, the progress callback is never called back. Even if I try to use a POST URL that contains a non-exsitent domain, nothing happens. I guess it is a deadlock, but I don't see how? The async Task's result is never used anywhere and it is not awaited for.
It is different from An async/await example that causes a deadlock because .Result is not used and the method is never awaited for, and also it seems that calling ConfigureAwait(false) ha no effect.
UPDATE: I have created a new github repo for this question, so anyone can test it:
https://github.com/nagylzs/csharp_http_post_example
UPDATE: Finally it works. ConfigureAwait is not needed. All UI update operations must be placed inside Invoke. I have updated the test repo to the working version. Also added TLSv1.2 support (which is disabled by default).
PostAsync in the code you've posted doesn't block (but it really never returns though!). It throws an exception:
System.InvalidOperationException: Cross-thread operation not valid: Control 'progressBar' accessed from a thread other than the thread it was created on.
That's the reason for the breakpoints that didn't worked for you. The right solution would be:
var progressContent = new ProgressableStreamContent(
content,
4096,
(sent, total) =>
{
Invoke((Action) (() => {
double percent = 100 * sent / total;
progressBar.Value = (int) percent;
}));
});
(either add Invoke or BeginInvoke to the callback)
The callbacks of the HTTP client are called on a background thread, and you have to put them into your window's even queue if you want them to access your UI controls.
.ConfigureAwait(false) has nothing to do with this issue, you shouldn't use it in UI context (quite the opposite: you want it to put the continuation onto the UI thread, so you shouldn't use it).
You need to change this:
client.PostAsync(opts.URL, progressContent, cancellationSource.Token)
to
client.PostAsync(opts.URL, progressContent, cancellationSource.Token).ConfigureAwait(false)
This is already discussed so you can find additional resources on the net, but this should be good starting point.

Which to use, WebClient.OpenRead, OpenReadAsync or OpenReadTaskAsync

I'm trying to understand the differences among WebClient.OpenRead, WebClient.OpenReadAsync and WebClient.OpenReadTaskAsync.
It looks like these have differences regarding blocking a thread, but I don't understand it well.
Could you please explain the differences? It would be great if you could give me some example (examples don't have to be sample code, but would be great if you could provide)
As you said, the difference is in thread blocking behavior. First one (OpenRead()) is thread blocking operation, other two - not. For example, let assume, that your network latency to reach google.com is 300ms. When you do var stream = webClient.OpenRead(#"https://www.google.com"); your application is "paused" for this 300ms, so code next to this line is not executed until your webClient return a stream to stream variable. This is calling Thread blocking.
When you do this in UI-thread (in example: in a button click handler) - your application become freezing and not responding to user actions. This is bad user experience, so never-ever call a thread blocking stuff in your UI. Here is example for console application:
var address = #"https://www.google.com/";
Console.WriteLine($"Opening stream from {address}");
using (var stream = webClient.OpenRead(address)) // this will block for 300ms
{
Console.WriteLine("Stream is open!");
// some other code
}
Second method (OpenReadAsync()) is asynchronous and return nothing immediately after a call, so your thread is not blocked. After awhile (300ms) OpenReadCompleted event will be raised by your webClient and all attached listeners will handle opened stream one-by-one. Here is an example:
public partial class MainForm : Form
{
private WebClient _client = new WebClient();
public MainForm()
{
InitializeComponents();
_client.OpenReadCompleted += OpenReadCompletedHandler;
}
private void ButtonClickHandler(object sender, EventArgs e)
{
_client.OpenReadAsync(#"https://www.google.com/");
}
private void OpenReadCompletedHandler(object sender, OpenReadCompletedEventArgs e)
{
// this event will be raiesed 300ms after 'Button' click
var stream = e.Result; // <- here is your stream
// some other code
}
}
The last one (OpenReadTaskAsync()) is all about TPL (Task Parallel Library) and async/await keywords. It runs all stuff in a Task which is returned by this method. Here is an example:
public partial class MainForm : Form
{
private WebClient _client = new WebClient();
public MainForm()
{
InitializeComponents();
}
private async void ButtonClickHandler(object sender, EventArgs e)
{
// after 'await' keyword, execution will be returned from this method immediately
// meanwhile, actual acquiring of 'stream' is running in a background thread
using (var stream = await _client.OpenReadTaskAsync(#"https://www.google.com/"))
{
// after 300ms, this code will be continued in UI thread
// result will be automaticly unpacked to 'stream' variable
// some other code
}
}
}
Hope this helps.
I'd suggest WebClient is more or less obsolete now, HttpClient is more appropriate for anything targeting .NET framework 4.5+ or .NET core. Just watch out that the latter does not automatically throw exceptions on HTTP error codes (400+).

Calling async method on button click

I created Windows Phone 8.1 project and I am trying to run async method GetResponse<T>(string url) on button click and waiting for the method to finish, but method is never finishing. Here is my code:
private void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
task.Wait();
var items = task.Result; //break point here
}
public static async Task<List<T>> GetResponse<T>(string url)
{
List<T> items = null;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
try
{
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
string text = strReader.ReadToEnd();
items = JsonConvert.DeserializeObject<List<T>>(text);
}
catch (WebException)
{
throw;
}
return items;
}
It will hang on task.Wait().
I changed my button click method to async and used await before the async method and I get the result(await GetResponse<string>("url")). What is wrong with Task<List<string>> task = GetResponse<string>("url")?
What am I doing wrong?
Thanks for the help!
You're the victim of the classic deadlock. task.Wait() or task.Result is a blocking call in UI thread which causes the deadlock.
Don't block in the UI thread. Never do it. Just await it.
private async void Button_Click(object sender, RoutedEventArgs
{
var task = GetResponseAsync<MyObject>("my url");
var items = await task;
}
Btw, why are you catching the WebException and throwing it back? It would be better if you simply don't catch it. Both are same.
Also I can see you're mixing the asynchronous code with synchronous code inside the GetResponse method. StreamReader.ReadToEnd is a blocking call --you should be using StreamReader.ReadToEndAsync.
Also use "Async" suffix to methods which returns a Task or asynchronous to follow the TAP("Task based Asynchronous Pattern") convention as Jon says.
Your method should look something like the following when you've addressed all the above concerns.
public static async Task<List<T>> GetResponseAsync<T>(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
string text = await strReader.ReadToEndAsync();
return JsonConvert.DeserializeObject<List<T>>(text);
}
This is what's killing you:
task.Wait();
That's blocking the UI thread until the task has completed - but the task is an async method which is going to try to get back to the UI thread after it "pauses" and awaits an async result. It can't do that, because you're blocking the UI thread...
There's nothing in your code which really looks like it needs to be on the UI thread anyway, but assuming you really do want it there, you should use:
private async void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
var items = await task;
// Presumably use items here
}
Or just:
private async void Button_Click(object sender, RoutedEventArgs
{
var items = await GetResponse<MyObject>("my url");
// Presumably use items here
}
Now instead of blocking until the task has completed, the Button_Click method will return after scheduling a continuation to fire when the task has completed. (That's how async/await works, basically.)
Note that I would also rename GetResponse to GetResponseAsync for clarity.
#ChrisWalsh: If you use Task.Run() and call the async task inside that function, the task will run on a new UI thread and prevent blocking your UI.
use below code
Task.WaitAll(Task.Run(async () => await GetResponse<MyObject>("my url")));

Accessing controls through threads

I use WebBrowser Control in c# to check a couple search engine results but sometime for no reason it gets stuck. My first thought was to make each searching function as a thread and use thread.abort() from a timer but I just couldn't handle with the UI Controls (including the WebBrowser) no matter what I've tried.
Anyone has a solution for me? an example would be great cause I already tried so many things and I keep getting all these exceptions.
I believe you can use WebRequest in a Background worker & avoid the difficulties of dealing with the COM threading model. You can use WebRequest.Timeout to handle anything you think is taking too long.
Try something like this:
static void Main(string[] args)
{
BackgroundWorker bg = new BackgroundWorker();
bg.DoWork += backgroundWorker_DoWork;
bg.RunWorkerAsync(new List<object> { "http://download.thinkbroadband.com/512MB.zip" });
while (true) {}
}
private static void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Starting operation.");
BackgroundWorker bw = sender as BackgroundWorker;
List<object> args = (List<object>)e.Argument;
var url = (string)args[0];
WebRequest request = WebRequest.Create(url);
request.Timeout = 300;
try
{
WebResponse response = request.GetResponse();
Console.WriteLine("Request successful.");
}
catch (Exception ex)
{
Console.WriteLine("Request timed out.");
}
}
WebBrowser is a component you should use when you want to embed an instance of IE into your presentation layer. Unless you need this, you can use something more lightwweight.
Use the control's .Invoke() method: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
There are other methods you can use too: .BeginInvoke and .EndInvoke for asynchronous invokes.
if(control.InvokeRequired)
{
control.Invoke(control.delegate);
}

UnauthorizedAccessException: Invalid cross-thread access in Silverlight application (XAML/C#)

Relatively new to C# and wanted to try playing around with some third party web service API's with it.
Here is the XAML code
<Grid x:Name="ContentGrid" Grid.Row="1">
<StackPanel>
<Button Content="Load Data" Click="Button_Click" />
<TextBlock x:Name="TwitterPost" Text="Here I am"/>
</StackPanel>
</Grid>
and here is the C# code
private void Button_Click(object sender, RoutedEventArgs e)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://api.twitter.com/1/users/show/keykoo.xml");
request.Method = "GET";
request.BeginGetResponse(new AsyncCallback(twitterCallback), request);
}
private void twitterCallback(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
TextReader reader = new StreamReader(response.GetResponseStream());
string strResponse = reader.ReadToEnd();
Console.WriteLine("I am done here");
TwitterPost.Text = "hello there";
}
I'm guessing that this is caused by the fact that the callback executes on a separate thread than the UI? What is the normal flow to deal with these types of interactions in C#?
Thanks.
A useful way to get easy cross thread access with the CheckAccess call is to wrap up a utility method in a static class - e.g.
public static class UIThread
{
private static readonly Dispatcher Dispatcher;
static UIThread()
{
// Store a reference to the current Dispatcher once per application
Dispatcher = Deployment.Current.Dispatcher;
}
/// <summary>
/// Invokes the given action on the UI thread - if the current thread is the UI thread this will just invoke the action directly on
/// the current thread so it can be safely called without the calling method being aware of which thread it is on.
/// </summary>
public static void Invoke(Action action)
{
if (Dispatcher.CheckAccess())
action.Invoke();
else
Dispatcher.BeginInvoke(action);
}
}
then you can wrap any calls that update the UI where you may be on a background thread like so:
private void twitterCallback(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
TextReader reader = new StreamReader(response.GetResponseStream());
string strResponse = reader.ReadToEnd();
UIThread.Invoke(() => TwitterPost.Text = "hello there");
}
This way you don't have to know whether you are on a background thread or not and it avoids the overhead of adding methods to every control to check this.
Another way to do this is to use the this.Dispatcher.CheckAccess() function. It is the same as what you get in WPF but it doesn't show up in your intellisense so you may not have seen it. What you do is check if you have access to the UI thread, and if you don't you recursively call yourself back on the UI thread.
private void twitterCallback(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
TextReader reader = new StreamReader(response.GetResponseStream());
string strResponse = reader.ReadToEnd();
Console.WriteLine("I am done here");
////TwitterPost.Text = "hello there";
postMyMessage(TwitterPost.Text);
}
private void postMyMessage(string text)
{
if (this.Dispatcher.CheckAccess())
TwitterPost.Text = text;
else
this.Dispatcher.BeginInvoke(new Action<string>(postMyMessage), text);
}
This is just a very simple example and can get unmanageable once you have more than a few controls. For some WPF/Silverlight stuff i have used a generic function which also takes the UI control to be updated as well as the update itself, that way i don't have one of these functions for every control on the page that may need updating from the results of a background thread.
As you guessed, silverlight executes all web request asynchronously on a separated thread to avoid freezing the UI.
To dispatch messages to the UI thread you can use the Deployment.Current.Dispatcher.BeginInvoke(delegate, object[]) method.

Categories

Resources