Task await fails - c#

I am starting a HubConnection. First I get a new Instance of HubConnection,
afterwards I create a new IHubProxy with Name = "fileHub" so far so good.
My problem is located at (or in) the awaitable Function ContinueWith, I try to start the Connection, and write to the console wheater the start was successful or not.
The connection.Start() is successful and "Connected" is written to the console.
Code added after the Console.WriteLine("Connected") is executed without problems, too.
But the Task never finishes, so the Client Class calling the HandleSignalRAsync() Method waits for the completion unsuccessfully.
Adding a return; or task.dispose(); does not solve my issue.
public static async Task HandleSignalRAsync()
{
connection = new HubConnection("http://localhost:12754");
myHub = connection.CreateHubProxy("fileHub");
await connection.Start().ContinueWith(
task =>
{
if (task.IsFaulted)
{
var ex = task.Exception.GetBaseException();
Console.WriteLine("There was an error opening the connection: {0}", ex);
}
else
{
Console.WriteLine("Connected");
}
});
}
I call the Method with the TaskAwaiter in another Class of my Solution:
Functions.HandleSignalRAsync().GetAwaiter().GetResult();
calling it with:
Functions.HandleSignalRAsync().Wait();
does not work, too.

But the Task never finishes
Because both examples synchronously block, causing your code to deadlock. You shouldn't be blocking on async code.
You'll need to properly asynchronously wait on HandleSignalRAsync:
await Functions.HandleSignalRAsync().ConfigureAwait(false);
If you're already using async-await, there's no advantage to use a continuation style with ContinueWith, you can simply wrap the execution in a try-catch statement and await inside:
try
{
await connection.Start().ConfigureAwait(false);
Console.WriteLine("Connected");
}
catch (Exception e)
{
Console.WriteLine("There was an error opening the connection: {0}", e);
}

Related

Cancellation doesn't go as expected

The while loop currently blocks the thread as it retries forever until the connection is established. I expect it to retry forever but it shouldn't block the thread (just like if we call StartAsync without awaiting it) and make it possible for us to call StopAsync during StartAsync's execution and cancel that process of retrying forever. It would require passing a CancellationToken to ConnectAsync too.
await client.StartAsync(); // do not block
await Task.Delay(5000);
await client.StopAsync();
I was thinking of moving the CancellationToken before the while loop and pass it to the while loop as well as loop until while (!await ConnectAsync().ConfigureAwait(false) && !_tokenSource.IsCancellationRequested) and then wrap that logic into a Task.Run to prevent blocking. What do you think?
Full code (#GeneralClient2 class)
public async Task StartAsync()
{
// Prevent a race condition
await _semaphore.WaitAsync().ConfigureAwait(false);
try
{
if (IsRunning)
{
return;
}
while (!await ConnectAsync().ConfigureAwait(false))
{
}
IsRunning = true;
Debug.Assert(_clientWebSocket != null);
_tokenSource = new CancellationTokenSource();
_processingSend = ProcessSendAsync(_clientWebSocket, _tokenSource.Token);
_processingData = ProcessDataAsync(_tokenSource.Token);
_processingReceive = ProcessReceiveAsync(_clientWebSocket);
}
finally
{
_semaphore.Release();
}
}
public async Task StopAsync()
{
if (!IsRunning)
{
return;
}
_logger.LogDebug("Stopping");
try
{
if (_clientWebSocket is { State: not (WebSocketState.Aborted or WebSocketState.Closed or WebSocketState.CloseSent) })
{
await _clientWebSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None).ConfigureAwait(false);
}
}
catch
{
}
await _processingReceive.ConfigureAwait(false);
_logger.LogDebug("Stopped");
}
private async ValueTask<bool> ConnectAsync()
{
_logger.LogDebug("Connecting");
var ws = new ClientWebSocket();
try
{
await ws.ConnectAsync(new Uri(_url), CancellationToken.None).ConfigureAwait(false);
Connected?.Invoke(this, EventArgs.Empty);
}
catch (Exception) // WebSocketException or TaskCanceledException
{
ws.Dispose();
return false;
}
_clientWebSocket = ws;
_logger.LogDebug("Connected");
return true;
}
The while loop currently blocks the thread
Are you sure? because ClientWebSocket.ConnectAsync explicitly states
This operation will not block. The returned Task object will complete
after the connect request on the ClientWebSocket instance has
completed.
So ConnectAsync should not block, and therefore neither the while loop. But even if it is non-blocking it might still consume significant CPU usage. Or something might throw before you even call ClientWebSocket.ConnectAsync, and since you just eat all the exceptions you will never know.
make it possible for us to call StopAsync during StartAsync's execution and cancel that process of retrying forever
You should be careful when stopping some service in a threaded or async context. Since some other task might find that the needed resource have been disposed.
while (!await ConnectAsync().ConfigureAwait(false) && !_tokenSource.IsCancellationRequested)
The problem with this is that the loop will exit and continue running the method if no connection has been established. Because of this it is recommended to use ThrowIfCancellationRequested, even if it is somewhat painful to use exceptions for flow control.
My recommendations would be to make StartAsync take a cancellationToken that aborts the connection process. This method should return an object representing the connection, i.e. Task<MyConnection>. This connection object should be disposable. Stopping the connection could be done like
// Create connection
var cts = new CancellationTokenSource();
var myStartAsyncConnectionTask = StartAsync(cts.Token);
// Close connection
cts.Cancel();
try{
var myConnection = await myStartAsyncConnectionTask;
myConnection.Dispose();
}
catch(OperationCancelledException)
{
...
}
That should work regardless of what state the connection is in. If a connection has been established the cancellation will do nothing, and the object will be disposed. If it has failed to connect, awaiting the task should throw. Note that StartAsync need to be written so that it cleans up any created resource in case the method throws any exception at any stage.

async/wait: bubbleup exceptions?

This pertains to a program that has to collect some info from a server on the web.
Question: How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
As depicted here, they do not. ErrorHandler never gets hit.
My Main program:
....
try
{
Task.Run(() => CheckAsync());
// bubble to here?
ReadConfiguration();
}
catch (Exception ex)
{
// gather and handle all exceptions here
ErrorHandler.NotifyMe(new[] { "some message" }, ErrorType.Stop); // never gets hit
}
public async Task CheckAsync()
{
await GetServerResponseAsync("slm_check"); // may throw exception
...
if (.....)
throw new Exception("...");
...
}
public async Task GetServerResponseAsync(string command)
{
...
// client = HttpClient()
using (apacheResponse = await client.GetAsync(ServerUrl + "...."))
{
if (....)
throw new Exception("Internal web server error", new Exception("Maybe MySQL server is down"));
using (HttpContent content = apacheResponse.Content)
{
if ( ....)
throw new Exception("error message");
}
}
}
How can I get the exceptions from GetServerResponseAsync and Check to bubble up through CheckAsync to the Main program?
Use await to consume your tasks, instead of ignoring them.
Specifically, this line:
Task.Run(() => CheckAsync());
is getting a Task back from the Task.Run method, which is then ignored. Instead of ignoring that task, the code should be awaiting it:
await Task.Run(() => CheckAsync());
As other commenters have pointed out, the Task.Run here doesn't really make sense. If your operation is asynchronous, it shouldn't need to also run on a background thread. Usually. :) So if you take out the Task.Run, your code would look like:
await CheckAsync();
which will properly propagate the exception.

Async Await Equivalent

When I run this code everything works fine:
public async void InvokePlugin(MyObject xTask)
{
try
{
var hndlr = new TimeoutHandler(RunTask);
var asyncResult = hndlr.BeginInvoke(xTask, null, new object());
if (!asyncResult.AsyncWaitHandle.WaitOne(xTask.Timeout, false))
{
throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
}
hndlr.EndInvoke(asyncResult);
}
catch (Exception ex)
{
//Handle Exceptions
}
}
private delegate void TimeoutHandler(MyObject xTask);
I want to update this code to use Async/Await. I tried doing it like this:
public async void InvokePlugin(MyObject xTask)
{
try
{
var runTask = Task.Run(() => { RunTask(xTask); });
if (await Task.WhenAny(runTask, Task.Delay(xTask.Timeout)) == runTask)
{
// Task completed within timeout.
// Consider that the task may have faulted or been canceled.
// We re-await the task so that any exceptions/cancellation is rethrown.
await runTask;
}
else
{
throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
}
}
catch (Exception ex)
{
//Handle Exceptions
}
}
...but it's not working. Clearly I'm doing somethign wring. It does call the RunTask Method and executes the first 2 lines fine but then it just ends and I can't seem to catch the exception in either the TaskRun method or code above. All I see in the Output windows is "Program has exited with code 0 (0x0)."
If the experts out there can either point me to what I'm doing wrong or give me suggestions as to how I can catch the exception and handle it I would be very grateful.
Also if you feel I missed any important details please ask and I will update my question.
Usually I'd say if it works don't fix it but in this case I'm trying to rearchitect a bit to allow for some enhancements so here I am.
Change async void to async Task. See my article on async best practices for more information.
After you do this, consume it asynchronously:
await the task, allowing async to grow.
async and await will naturally grow upward through your code base, until they reach Main, which cannot be async.
In your Main method, call GetAwaiter().GetResult() on the "top" task.
Blocking on asynchronous code is generally not a good idea, but blocking on a single task in a Console app's Main method is an exception to that rule.

calling WCF method asynchronously

I'm looking for feedback on the following code. I've read here that I should avoid use of async void.
So as a result I've implemented the following in a method..
foreach (var sample in returns)
{
_logger.Debug("Calling async method");
var resultFromMethodCall = CallMethodAsync(uploadReturn);
_logger.Debug("Continuing....");
}
async Task<Tuple<bool,long>> CallMethodAsync(Sample sampleReturn)
{
try
{
Service1Client client = new Service1Client();
Tuple<bool, long> results = await client.ValidateSampleReturnAsync(sampleReturn);
_logger.Debug("call to Sample Return validator completed for sample: {0}", results.Item2);
return results;
}
catch (Exception ex)
{
_logger.Error(ex, "Error occured while calling WCF service");
return new Tuple<bool, long>(false, sampleReturn.Id);
}
}
When I do nothing with the returned variable resultFromMethodCall, the logging indicates the all is working as I expect. However when I log out items from the variable resultFromMethodCall, it appears that its now running synchronously as it waits for the object to be returned from the call.
Am I missing something obvious here? Or am I completely misunderstanding how this works.
CallMethodAsync is correct.
If you don't await (or Wait) resultFromMethodCall execution will continue while that task is still running. Whether you should allow that depends on what you want to happen.
However when I log out items from the variable resultFromMethodCall it appears that its now running synchronously as it waits for the object to be returned from the call.
If you're using resultFromMethodCall.Result to get the items, then yes, it's blocking and running synchronously.
If you're using await resultFromMethodCall to get the items, then no, it's not running synchronously. However, it is running sequentially - meaning that the method will "pause" at the await and only continue when the resultFromMethodCall task completes.

Creating an async webservice method

I've tried to read up on async methods and am now trying to create my own async method. The method is a webservice call that returns a list of error logs. I'm not sure that I've understood correctly so I thought I'd share my code to see if I should do anything different.
All I want the code to do is return a list of errorlogs by calling a method GetAllErrorLogs(), that is a synchronized method. Since it can take a second to fetch all the error logs I want to have the opportunity to do other stuff once I called the GetAllErrorLogs() method. Here is the code.
[WebMethod]
public async Task<List<ErrorLog>> GetAllErrorLogs()
{
List<ErrorLog> errorLogs = new List<ErrorLog>();
await System.Threading.Tasks.Task.Run(() => {
errorLogs = ErrorLogRepository.GetAllErrorLogs();
});
if (errorLogs == null)
return new List<ErrorLog>();
return errorLogs;
}
Thanks!
I recently gave a talk at ThatConference on async on the server side, and I address this issue in the slides.
On the server side, you want to avoid the use of Task.Run and other constructs that queue work to the thread pool. As much as possible, keep thread pool threads available for handling requests.
So, ideally your repository would have an asynchronous method GetAllErrorLogsAsync, which would itself be asynchronous. If GetAllErrorLogs cannot be asynchronous, then you may as well just call it directly (removing the await Task.Run).
Since it can take a second to fetch all the error logs I want to have the opportunity to do other stuff once I called the GetAllErrorLogs() method.
If you have a GetAllErrorLogsAsync available, then this can easily be done using Task.WhenAll. However, if GetAllErrorLogs is synchronous, then you can only do this by doing parallel work in your request (e.g., multiple calls to Task.Run followed by Task.WhenAll).
Parallel code on the server must be approached with great trepidation. It is only acceptable in a very limited set of scenarios. The entire point of async on the server side is to use fewer threads per request, and when you start parallelizing, you're doing the opposite: multiple threads per request. This is only appropriate if you know your user base is very small; otherwise, you'll kill your server scalability.
I found this great codeproject detailed article about how to achieve that
http://www.codeproject.com/Articles/600926/Asynchronous-web-services-call-in-ASP-NET
**This is potentially wrong, read comments or spinoff question at HttpContext.Current after an await
If ErrorLogRepository.GetAllErrorLogs() is not thread-safe, it will cause weird bugs and potentially exception out. Make sure your code is ready for multi-threaded operation before switching to async methods, this is obviously very trivial advice but often overlooked. For example, if you reference HttpContext.Current in your methods, your code will die in the async method, and sometimes even AFTER the await. The reason is that the code within the async block will potentially be run on a separate thread, which will not have access to the same HttpContext.Current thread-static property, and await gets compiled into two methods. All code before an await gets run on one thread, and then calls the code after an await keyword as a continuation, but potentially on yet another thread. So sometimes your code will even work in an async block, only to choke unexpectedly after it gets "out" of the async back to what you think is a synchronous part of your code (but in reality everything after an await keyword is already not guaranteed to be the original thread).
Here is some production code...
using System.Web.Http;
using AysncTask = System.Threading.Tasks.Task;
public class myController : ApiControllerBase
{
[HttpPut]
[Route("api/cleardata/{id}/{requestId}/")]
public async AysncTask ClearData(Guid id, Guid requestId)
{
try
{
await AysncTask.Run(() => DoClearData(id, requestId));
}
catch (Exception ex)
{
throw new Exception("Exception in myController.ClearData", ex);
}
}
}
Handling Async exceptions is also VERY VERY important.. although this is for a windows console app, the same principles should apply.
source: https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncAndExceptions
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (s, e) => Log("*** Crash! ***", "UnhandledException");
TaskScheduler.UnobservedTaskException += (s, e) => Log("*** Crash! ***", "UnobservedTaskException");
RunTests();
// Let async tasks complete...
Thread.Sleep(500);
GC.Collect(3, GCCollectionMode.Forced, true);
}
private static async Task RunTests()
{
try
{
// crash
// _1_VoidNoWait();
// crash
// _2_AsyncVoidAwait();
// OK
// _3_AsyncVoidAwaitWithTry();
// crash - no await
// _4_TaskNoWait();
// crash - no await
// _5_TaskAwait();
// OK
// await _4_TaskNoWait();
// OK
// await _5_TaskAwait();
}
catch (Exception ex) { Log("Exception handled OK"); }
// crash - no try
// await _4_TaskNoWait();
// crash - no try
// await _5_TaskAwait();
}
// Unsafe
static void _1_VoidNoWait()
{
ThrowAsync();
}
// Unsafe
static async void _2_AsyncVoidAwait()
{
await ThrowAsync();
}
// Safe
static async void _3_AsyncVoidAwaitWithTry()
{
try { await ThrowAsync(); }
catch (Exception ex) { Log("Exception handled OK"); }
}
// Safe only if caller uses await (or Result) inside a try
static Task _4_TaskNoWait()
{
return ThrowAsync();
}
// Safe only if caller uses await (or Result) inside a try
static async Task _5_TaskAwait()
{
await ThrowAsync();
}
// Helper that sets an exception asnychronously
static Task ThrowAsync()
{
TaskCompletionSource tcs = new TaskCompletionSource();
ThreadPool.QueueUserWorkItem(_ => tcs.SetException(new Exception("ThrowAsync")));
return tcs.Task;
}
internal static void Log(string message, [CallerMemberName] string caller = "")
{
Console.WriteLine("{0}: {1}", caller, message);
}
}
}

Categories

Resources