client.GetStreamAsync(url) is freezing my UI - c#

So I'm listening to an Server side event with my code to just write it on the console (for now) but it seems that this is making my window's form UI freeze
The code in question (which I'm calling from the main form's function)
static async Task hello()
{
HttpClient client = new HttpClient();
//client.Timeout = TimeSpan.FromSeconds(5);
while (true)
{
try
{
Console.WriteLine("Establishing connection");
using (var streamReader = new StreamReader(await client.GetStreamAsync(url)))
{
while (!streamReader.EndOfStream)
{
var message = await streamReader.ReadLineAsync();
Console.WriteLine(message.ToString());
}
}
}
catch (Exception ex)
{
//Here you can check for
//specific types of errors before continuing
//Since this is a simple example, i'm always going to retry
Console.WriteLine($"Error: {ex.Message}");
Console.WriteLine("Retrying in 5 seconds");
await Task.Delay(TimeSpan.FromSeconds(5));
}
}
}
Thanks in advance

I've solved the problem, it appears that async/await task freezes the GUI. To stop this from happening you need to use Task.Run(() => your_function()); when you call an async function
This question might be a possible duplicate of: GUI freezes when using async/await ... so go there if you want to find a bit more knowledge about the subject

Related

Output of one Task input to other Task

In Winforms, I have following: ProcessClientAsync adds an element to a ConcurrentDictionary. How can I ensure that the forloop runs after the Task.Run(). I tried to remove ConfigureAwait but it freezes the UI.
public async Task Listen(int port)
{
try
{
IPAddress serverAddress = IPAddress.Parse("127.0.0.1"); // localhost
_listener = new TcpListener(serverAddress, port);
_listener.Start();
while (true)
{
TcpClient tcpClient = await _listener.AcceptTcpClientAsync();
await Task.Run(() => ProcessTcpClientAsync(tcpClient).ConfigureAwait(false));
_statusText.StatusUpdate = "number of users are " + _mapClient.GetUsers().Count;
}
}
catch (SocketException ex)
{
string message = string.Format("Error listening on port {0}. Make sure IIS or another application is not running and consuming your port.", port);
throw new Exception(message, ex);
}
}
private async Task<string> ProcessTcpClientAsync(TcpClient tcpClient)
{
string key = string.Empty;
WebSocket webSocket = null;
try
{
if (_isDisposed)
return string.Empty;
// this worker thread stays alive until either of the following happens:
// Client sends a close conection request OR
// An unhandled exception is thrown OR
// The server is disposed
// get a secure or insecure stream
NetworkStream stream = tcpClient.GetStream();
WebSocketHttpContext context = await _webSocketServerFactory.ReadHttpHeaderFromStreamAsync(stream);
if (context.IsWebSocketRequest)
{
key = GetKeyFromContext(context);
// _statusText.StatusUpdate = "Connection from origin.";
webSocket = await _webSocketServerFactory.AcceptWebSocketAsync(context);
//_statusText.StatusUpdate = "Connection accepted.";
await RespondToWebSocketRequestAsync(tcpClient, key, webSocket);
}
else
{
//_statusText.StatusUpdate = "Http header contains no web socket upgrade request. Ignoring...";
}
}
catch (Exception ex)
{
}
finally
{
try
{
await webSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "Closed in server by the client", CancellationToken.None);
tcpClient.Client.Close();
tcpClient.Close();
}
catch (Exception ex)
{
}
}
return key;
}
You could await the Task.Run, but make sure to make the parent method async
await Task.Run(() => ProcessClientAsync(client).ConfigureAwait(false));
This will wait for the async task to complete, and then execute the rest of the code. I would suggest learning a bit more about async/await.
To block the ProcessClientAsync call you can do the following:
Task.Run(() => ProcessClientAsync(client)).Wait();
If you want to access the result of ProcessClientAsync:
Task<TResult> task = null;
Task.Run(() => task = ProcessClientAsync(client)).Wait();
// task.Result contains the result
Even if this works, it's recommended to await tasks rather than blocking with wait.
Quiz, what is the type of the variable x below?
var x = Task.Run(() => Task.Delay(1000).ConfigureAwait(false));
The type is not Task. It is Task<ConfiguredTaskAwaitable>. That internal ConfigureAwait(false) call is not only meaningless, but it also created a
unexpected situation where the return value must now be awaited twice:
await await x;
Don't do this. If you have to use ConfigureAwait(false) for some reason, then you are expected to await the result immediately. Don't pass ConfiguredTaskAwaitable structs around. My advice is to search your code for more instances of this anti-pattern, and eliminate them.
If you don't want to mark the parent method as async, then you could use the following instead:
Task.Wait(Task.Run(() => ProcessClientAsync(client)));
This method has several overloads that allow for configurability of cancellation and timeouts as well:
https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.wait?view=netframework-4.8
Also, if your code actually says while (true) and has no break conditions, then it will never exit.

Inform that a long running async task is in progress - the right way

I have a console program which sends async HTTP requests to an external web API. (HttpClient.GetAsync());)
These tasks can take several minutes to complete - during which I'd like to be able to show to the user that the app is still running - for example by sending Console.WriteLine("I ain't dead - yet") every 10 seconds.
I am not sure how to do it right, without the risk of hiding exceptions, introducing deadlocks etc.
I am aware of the IProgress<T>, however I don't know whether I can introduce it in this case. I am await a single async call which does not report progress. (It's essentially an SDK which calls httpClient GetAsync() method
Also:
I cannot set the GUI to 'InProgress', because there is no GUI, its a console app - and it seems to the user as if it stopped working if I don't send an update message every now and then.
Current idea:
try
{
var task = httpClient.GetAsync(uri); //actually this is an SDK method call (which I cannot control and which does not report progress itself)
while (!task.IsCompleted)
{
await Task.Delay(1000 * 10);
this.Logger.Log(Verbosity.Verbose, "Waiting for reply...");
}
onSuccessCallback(task.Result);
}
catch (Exception ex)
{
if (onErrorCallback == null)
{
throw this.Logger.Error(this.GetProperException(ex, caller));
}
this.Logger.Log(Verbosity.Error, $"An error when executing command [{action?.Command}] on {typeof(T).Name}", ex);
onErrorCallback(this.GetProperException(ex, caller));
}
Let me tidy this code up a bit for you
async Task Main()
{
var reporter = new ConsoleProgress();
var result = await WeatherWaxProgressWrapper(() => GetAsync("foo"), reporter);
Console.WriteLine(result);
}
public async Task<int> GetAsync(string uri)
{
await Task.Delay(TimeSpan.FromSeconds(10));
return 1;
}
public async Task<T> WeatherWaxProgressWrapper<T>(Func<Task<T>> method, System.IProgress<string> progress)
{
var task = method();
while(!task.IsCompleted && !task.IsCanceled && !task.IsFaulted)
{
await Task.WhenAny(task, Task.Delay(1000));
progress.Report("I ain't dead");
}
return await task;
}
public class ConsoleProgress : System.IProgress<string>
{
public void Report(string value)
{
Console.WriteLine(value);
}
}
You could have a never-ending Task as a beacon that signals every 10 sec, and cancel it after the completion of the long running I/O operation:
var beaconCts = new CancellationTokenSource();
var beaconTask = Task.Run(async () =>
{
while (true)
{
await Task.Delay(TimeSpan.FromSeconds(10), beaconCts.Token);
Console.WriteLine("Still going...");
}
});
await LongRunningOperationAsync();
beaconCts.Cancel();
You are looking for System.Progress<T>, a wonderful implementation of IProgress.
https://learn.microsoft.com/en-us/dotnet/api/system.progress-1
You create an object of this class on the "UI thread" or the main thread in your case, and it captures the SynchronizationContext for you. Pass it to your worker thread and every call to Report will be executed on the captured thread, you don't have to worry about anything.
Very useful in WPF or WinForms applications.

await function not executed?

I found this piece of code online on csharpstar.com as I was trying to understand more about async and await. Can someone help me understand why when I debug it I reach the line where await client.GetStringAsync ("http://www.CsharpStar.com"); is assigned to result, and then the debugger doesn't move onto the next line from try, and doesn't enter catch either, it just seems to resume execution from Main?
If the await line throws a timeout error or something should it not go to catch?
Also weird that when control/flow resumes to Main, it doesn't go through line ReadLine(), which it executed earlier (before entering the GetSite method), however the ReadLine() takes effect after exiting the GetSite method.
What is the explanation of await function disrupting the regular flow? Why can't I debug the next line, so I can see the value stored into the result variable?
using System;
using System.Net.Http;
using System.Threading.Tasks;
using static System.Console;
namespace CSharpStar
{
class Program
{
static void Main(string[] args)
{
Task.Factory.StartNew(() => GetSite());
ReadLine(); // **...to here (2)**
}
private async static Task GetSite()
{
HttpClient client = new HttpClient();
try
{
var result = await client.GetStringAsync ("http://www.CsharpStar.com"); // **debugger jumps from here (1)**
WriteLine(result);
}
catch (Exception exception)
{
try
{
//This asynchronous request will be invoked if the first request is failed.
var result = await client.GetStringAsync ("http://www.easywcf.com");
WriteLine(result);
}
catch
{
WriteLine("Entered Catch Block");
}
finally
{
WriteLine("Entered Finally Block");
}
}
}
}
}
Not sure what article you were reading, but you should stop there. Task.Factory.StartNew is not recommended, but Task.Run is.
What is happening is that the control is returned to the caller (Main in this case) as soon as the await keyword is reached. Since you are not awaiting for the Task to finish, the program goes on to the Console.WriteLine line. This is known as a fire and forget call.
If you want to wait for the function to finish, change your code to:
static void Main(string[] args)
{
// change to the recommended Task.Run method
var task = Task.Run(() => GetSite());
task.GetAwaiter().GetResult(); // block until the Task is finished
ReadLine();
}

How to determine whether Task.Run is completed within a loop

This may be an odd question and it is really for my educational purpose so I can apply it in future scenarios that may come up.
I am using C#.
I am stress testing so this is not quite production code.
I upload data to my server via a web service.
I start the service off using a Task.Run.
I check to see if the Task is completed before allowing the next Run.Task to begin.
This is done within a loop.
However, because I am using a modular declared Task will not the result be affected?
I could declare a local Task.Run variable but I want to see how far I can get with this question 1st.
If the Task.Run can raise an event to say it is completed then this may not be an issue?
This is my code:
//module declaration:
private static Task webTask = Task.Run(() => { System.Windows.Forms.Application.DoEvents(); });
//in a function called via a timer
if (webTask.IsCompleted)
{
//keep count of completed tasks
}
webTask = Task.Run(() =>
{
try
{
wcf.UploadMotionDynamicRaw(bytes); //my web service
}
catch (Exception ex)
{
//deal with error
}
);
IMO you do not need the timer. Using Task Continuation you subscribe to the done event:
System.Threading.Tasks.Task
.Run(() =>
{
// simulate processing
for (var i = 0; i < 10; i++)
{
Console.WriteLine("do something {0}", i + 1);
}
})
.ContinueWith(t => Console.WriteLine("done."));
The output is:
do something 1
do something 2
.
.
do something 9
do something 10
done
Your code could look like this:
var webTask = Task.Run(() =>
{
try
{
wcf.UploadMotionDynamicRaw(bytes); //my web service
}
catch (Exception ex)
{
//deal with error
}
}).ContinueWith(t => taskCounter++);
With task continuation you could even differentiate between failed and success process result, if you want to count only successfull tasks - using the TaskContinuationOptrions.
You can wait for your task to complete by awaiting your task like this
await webTask;
that will asynchronously wait for 'webTask' to complete. Instead of the timer you can use await Task.Delay which will asynchronously wait for the delay to expire. I would also consider making the wcf call asynchronous so you don't have to call inside Task.Run. See this question for some tips.
I'd rewrite the code as follows:
public async Task UploadAsync()
{
while(true)
{
await Task.Delay(1000); // this is essentially your timer
// wait for the webTask to complete asynchrnously
await webTask;
//keep count of competed tasks
webTask = Task.Run(() =>
{
try
{
// consider generating an asynchronous method for this if possible.
wcf.UploadMotionDynamicRaw(bytes); //my web service
}
catch (Exception ex)
{
//deal with error
}
});
}
}

Async/await and WebException handling

I am struggling around the (seems so) pretty famous problem of the exception handling by using the async/await pattern. Specifically my context is on a HTTP client, but I have also tried with a much simpler test, and it behaves the same.
Consider the below program, which is a super-simplified version of my original app's context.
class Program
{
static void Main(string[] args)
{
Test();
Console.Write("Press any key...");
Console.ReadKey();
Console.WriteLine();
}
static async void Test()
{
var c = new MyClient();
try
{
var uri = new Uri("http://www.google.com/"); //valid address
var s = await c.GetString(uri);
Console.WriteLine(s.Length);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
try
{
var uri = new Uri("http://www.foo.bah/"); //non-existent address
var s = await c.GetString(uri);
Console.WriteLine(s.Length);
}
catch (WebException ex)
{
Console.WriteLine(ex.Message);
}
}
}
class MyClient
{
public async Task<string> GetString(Uri uri)
{
var client = new HttpClient();
return await client.GetStringAsync(uri);
}
}
When the program starts, it downloads the first web site's page as a string, then displays its length: that's fine. Afterward, when the same operation is performed against an invalid address, the client raises a WebException (that's what I want), but it's not caught.
UPDATE: as "not caught", I mean that the code actually does not flow through the "catch" branch and silently displays the exception message. Instead, the exception is shown by the VS IDE, and the debugging breaks.
Any decent solution to catch the exception?
Many thanks in advance.
Although you have already figured out the exception is HttpRequestException not WebException, still I would like to highlight few important things about async-await operator usage.
async void is of type fire & forget and is only & only for event handlers.
As soon as compiler reaches first await operator inside async method control returns to the caller.
Debugging your code :-
Since you are using async void in Test method so the control returns to the caller and execution continues to line Console.Write("Press any key..."); without having any information about the Task and then you are waiting for the user input.
In the meanwhile response from awaited method comes and the execution continues inside Test method.
If you comment out the line Console.ReadKey(); inside main() OR user provides input immediately then you'll notice that response may or may not get printed. This is because you are not waiting on the Task getting executed you simply trusted on the user that he will not enter anything till your Task completes.
Solution:-
Solution is to return Task from Test() and then wait till it finishes, below is the updated code also note adding Async at the end of method name is the naming convention you must follow to save you from the headache of distinguishing between asynchronous and synchronous methods.
class Program
{
static void Main(string[] args)
{
Task task = TestAsync();
Console.Write("Press any key...");
task.wait();
//Console.ReadKey();
Console.WriteLine();
}
static async Task<string> TestAsync()
{
var c = new MyClient();
try
{
var uri = new Uri("http://www.google.com/"); //valid address
var s = await c.GetStringAsync(uri);
Console.WriteLine(s.Length);
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.Message);
}
try
{
var uri = new Uri("http://www.foo.bah/"); //non-existent address
var s = await c.GetStringAsync(uri);
Console.WriteLine(s.Length);
}
catch (HttpRequestException ex)
{
Console.WriteLine(ex.Message);
}
//to avoid compiler error
return null;
}
}
class MyClient
{
public async Task<string> GetStringAsync(Uri uri)
{
var client = new HttpClient();
return await client.GetStringAsync(uri);
}
}

Categories