I am coding an app (.Net 5) that needs to execute a few async methods. First it creates a Job, then it creates a Task within the job, then waits for the Task to complete, and finally deletes the Job. For context, these are Azure Batch Jobs and Tasks, but I only mention it to indicate that what I call a Task is not the .Net framework Task, but a plain class that just happens to be named that. Other than that, the fact that this is Azure Batch related makes no difference.
I have coded this method to do those steps:
private async Task ExecuteTask(string jobName, string taskName, CloudTask task)
{
string poolName = ConfigurationManager.AppSettings["PoolName"];
await taskScheduler.QueueBatchJob(poolName, jobName);
await taskScheduler.QueueBatchTask(jobName, task);
taskScheduler.WaitForTaskToComplete(jobName, taskName);
_ = this.taskScheduler.DeleteJob(jobName); // I am intentionally not awaiting here
}
The calling method has a try / catch, with extra code that may take a while to execute.
I am intentionally not awaiting the DeleteJob method as I don't need for that to be done in order to continue, and I also don't care if it fails. It's there for cleanup but there may later be another process to do proper cleanup.
Now, my question is, what happens if there actually is an error on that method? will it get catch by the parent try/catch if the parent method did not complete? I definitely do not want that so if that is the case, how can I ignore it?
I'll simply answer this question with some quotes from here:
Async void methods have different error-handling semantics. When an
exception is thrown out of an async Task or async Task method, that
exception is captured and placed on the Task object. With async void
methods, there is no Task object, so any exceptions thrown out of an
async void method will be raised directly on the
SynchronizationContext that was active when the async void method
started.
later in the same article
When you await a Task, the first exception is re-thrown, so you can catch the specific exception type (such as InvalidOperationException).
That basically means:
The Task returned by the method will contain the Exception but it will only be raised if you await it. Therefore not awaiting a Task results in all Exceptions being swallowed by the Task object.
Related
I am using a library which provides methods ending with ...Async and return Task. I am going to use these in a command line application. So I need to call them synchronously a lot.
C# of course does not allow calling these methods in Main method since you cannot use async modifier on Main method. Assume this is the task:
var task = datastore.Save(data);
I found several solutions like:
Tasks.WaitAll(task);
task.Wait();
however all these wrap thrown exceptions in AggregateException, I don't want that. I just want to say task.Result and I expect the original exception to be thrown.
When I use a method returning Task<TResult>, task.Result throws AggregateException even though there are no continuation tasks set. Why is this hapening?
I also have tried,
task.RunSynchronously();
it gives error:
RunSynchronously may not be called on a task not bound to a delegate, such as the task returned from an asynchronous method.
so I guess that's not for methods marked as async.
Any ideas on patterns using libraries designed for async apps in console apps where there is no asynchronous context?
I am going to use these in a command line application. So I need to call them synchronously a lot.
No, you don't. You can use async-await in a console application, you just need to make an async to sync transition at the very top. And you can do that by using Wait():
public static void Main()
{
MainAsync().Wait();
}
public static async Task MainAsync()
{
var datastore = …;
await datastore.SaveAsync();
}
Usually, combining await with Wait() is a bad idea (it can cause deadlocks), but it's the right solution here.
Note that if SaveAsync() throws an exception and you don't catch it, it will be rethrown as AggregateException from the Wait(). But you can catch it as the original exception in MainAsync() (because it doesn't use Wait()).
If you really wanted to get the first exception thrown directly, you could do something similar to what await does: task.GetAwaiter().GetResult(). Note that if the Task contains more than one exception, you will get only the first one (but the same applies to await).
Since C# 7.1, you can make your Main method async and the compiler will write the transition code for you:
public static async Task Main()
{
var datastore = …;
await datastore.SaveAsync();
}
When I use a method returning Task<TResult>, task.Result throws AggregateException even though there are no continuation tasks set. Why is this happening?
This has nothing to do with continuations. A single Task can represent multiple operations, and each of them can throw an exception. Because of that, Task methods always throw the exceptions wrapped in an AggregateException.
I also have tried task.RunSynchronously()
That doesn't make any sense. RunSynchronously() can only be used on Tasks that were created using the Task constructor. That's not the case here, so you can't use it. Tasks returned from async methods are always already started.
You can create a dummy Main
public static void Main()
{
MainAsync().Wait();
}
public static async Task MainAsync()
{
try {
var result = await dataStore.Save(data);
} catch(ExceptionYouWantToCatch e) {
// handle it
}
}
Also, see this answer: https://stackoverflow.com/a/9212343/1529246
As of C# 7.1 you can now declare the Main method as async. Just have to make sure your language is either set to default major version (which should work in VS 2019 now) or you can always target a specific version of the language.
See this link for details. Turn on async main
I've a stupid question to ask everyone to learn some best practises async return types in c#. I've a method like this:
public virtual async Task OnConnected(WebSocket socket, HttpContext context)
{
string connectionId = CreateConnectionId(context);
ConnectionManager.AddSocket(connectionId, socket);
//Question is here await what ?
}
I'd like to post to improve my application with a best performance with async and await. Pls don't thumb dowm my post. Can everyone suggest for me some resources best practices async return types?
Tks everyone.
If there is nothing to await and this is an overridden method. Just ignore the compiler warning (or pragma is out), and do nothing.
Why do nothing?
Because when you place an async on a method, the compiler automatically places any exception on the task for you.
or you could remove the async and return a completed task
public virtual Task OnConnected(WebSocket socket, HttpContext context)
{
string connectionId = CreateConnectionId(context);
ConnectionManager.AddSocket(connectionId, socket);
return Task.CompletedTask;
}
My advice would be to do the former, (do nothing)
However if you were to go the second approach it would be better to place any exceptions on the returned task just as the async and await pattern would.
public virtual Task OnConnected(WebSocket socket, HttpContext context)
{
try
{
string connectionId = CreateConnectionId(context);
ConnectionManager.AddSocket(connectionId, socket);
// more stuff
return Task.CompletedTask;
}
catch (Exception e)
{
return Task.FromException(e);
}
}
learn some best practises async return types in c#.
The first rule of async is "don't use it unless you need to." The second rule is "start with await, not async".
So this part of the question is incorrect:
//Question is here await what ?
Instead of starting with async and trying to "make the code asynchronous", you should start at the opposite end: identify what is naturally asynchronous (i.e., what you should await) first. You need something to await before making your code asynchronous.
You should use await if you have naturally-asynchronous work to do. This is usually I/O-based, so if you're calling an API or doing a database query, that would be something that can be made asynchronous. Once you identified the low-level I/O call, change the method to call the asynchronous version of that API, and add an await. The compiler will guide you from there, changing the method containing the await to be async and changing its return type from void to Task or T to Task<T>. Then let async/await grow naturally through your codebase.
If you do not have any asynchronous operation to be waited for, then you can make the method as a normal synchronous method. If you are making a method async, then it is good practice to name the method in such a way that it ends with ASYNC like OnConnectedAsync.
Generally you use async/await methods when you have any I/O operations (like file access, web download/upload) that can be done independent of your program logic. Simply put the control flow goes like,
main thread calls the async method to trigger the I/O operation
control is returned to main thread after an await is encountered; usually another thread is allotted to execute remaining statements of the async method
main thread continues to execute its statements until it requires the result of the I/O operation
main thread waits for the result of async method (i.e. result of I/O operation) by calling any wait or GetResult method of Task
once the main thread gets the results, it continues executing its statements
You can go through numerous tutorial videos on youtube on async/await.
So here is what I'm trying to achieve. I launch a task and don't do wait/result on it. To ensure that if launched task goes to faulted state (for e.g. say throw an exception) I crash the process by calling Environment FailFast in Continuation.
How the problem I'm facing is that If I ran below code, Inside ContinueWith, the status of the task (which threw exception) shows up as "RanToCompletion". I expected it to be Faulted State.
private Task KickOfTaskWorkAsync()
{
var createdTask = Task.Run(() => this.RunTestTaskAsync(CancellationToken.None).ConfigureAwait(false), CancellationToken.None);
createdTask.ContinueWith(
task => Console.WriteLine("Task State In Continue with => {0}", task.Status));
return createdTask;
}
private async Task RunTestTaskAsync(CancellationToken cancellationToken)
{
throw new Exception("CrashingRoutine: Crashing by Design");
}
This is really strange :( If I remove the 'ConfigureAwait(false)' inside Task.Run function call, the task does goes to Faulted state inside Continue with. Really at loss to explain what's going on and would appreciate some help from community.
[Update]: My colleague pointed out an obvious error. I am using ConfigureAwait while I make a call to RunTestAsync inside Test.Run even though I don't await it. In this case, ConfigureAwait doesn't return a Task to Task.Run. If I don't call ConfigureAwait, a Task does get returned and things work as expected.
Your error is a specific example of a broader category of mistake: you are not observing the Task you actually care about.
In your code example, the RunTestTaskAsync() returns a Task object. It completes synchronously (because there's no await), so the Task object it returns is already faulted when the method returns, due to the exception. Your code then calls ConfigureAwait() on this faulted Task object.
But all of this happens inside another Task, i.e. the one that you start when you call Task.Run(). This Task doesn't do anything to observe the exception, so it completes normally.
The reason you observe the exception when you remove the ConfigureAwait() call has nothing to do with the call itself. If you left the call and passed true instead, you would still fail to observe the exception. The reason you can observe the exception when you remove the call is that, without the call to ConfigureAwait(), the return value of the lambda expression is a Task, and this calls a different overload of Task.Run().
This overload is a bit different from the others. From the documentation:
Queues the specified work to run on the thread pool and returns a proxy for the task returned by function.
That is, while it still starts a new Task, the Task object it returns represents not that Task, but the one returned by your lambda expression. And that proxy takes on the same state as the Task it wraps, so you see it in the Faulted state.
Based on the code you posted, I would say that you shouldn't be calling Task.Run() in the first place. The following will work just as well, without the overhead and complication of the proxy:
static void Main(string[] args)
{
Task createdTask = RunTestTaskAsync();
createdTask.ConfigureAwait(false);
createdTask.ContinueWith(
task => Console.WriteLine("Task State In Continue with => {0}", task.Status)).Wait();
}
private static async Task RunTestTaskAsync()
{
throw new Exception("CrashingRoutine: Crashing by Design");
}
(I removed the CancellationToken values, because they have nothing at all to do with your question and are completely superfluous here.)
I have a theoretical question to you. What happens if I await the Result of a Task inside another task? I want to know if my current system will work afterwards.
A task gets launched and does some stuff. At some point that task might need another one in order to process data which the current task is not able to process by itself. So I use await to ensure that the current task won't continue as long as he has not the result of the helper task. But what happens if the helper fails? Will the current task remain locked?
Can I avoid this deadlock somehow (without changing the system itself - task inside task)?
A task gets launched and does some stuff. At some point that task might need another one in order to process data which the current task is not able to process by itself. So I use await to ensure that the current task won't continue as long as he has not the result of the helper task. But what happens if the helper fails? Will the current task remain locked?
The core idea behind async and await is that asynchronous code works just about the same as synchronous code.
So, if you have synchronous code like this:
void HelperMethod()
{
throw new InvalidOperationException("test");
}
void DoStuff()
{
HelperMethod();
}
then you would expect DoStuff to propagate the InvalidOperationException from the helper method. Similarly, that's what happens with asynchronous code:
async Task HelperMethodAsync()
{
throw new InvalidOperationException("test");
}
async Task DoStuffAsync()
{
await HelperMethodAsync();
}
That is, DoStuffAsync will also propagate the InvalidOperationException.
Now, it doesn't work exactly the same way, of course, since it must be asynchronous, but the general idea is that all your control flow such as try/catch, for loops, etc, all "just work" for asynchronous code very similarly to synchronous code.
What's actually going on is that when HelperMethod ends with an InvalidOperationException, the exception is caught and placed on the returned Task, and the task is completed. When the await in DoStuffAsync sees that the task has completed, it examines its exceptions and re-raises the first one (in this case, there is only one, the InvalidOperationException). And it re-raises it in a way that preserves the call stack on the exception. This in turn causes the Task returned from DoStuffAsync to be completed with that same exception.
So, under the covers async and await are doing a bit of work to ensure that you can just call other methods with await and use try/catch the same way as you would in synchronous code. But most of the time you don't have to be aware of that.
It's really easy to test. For example:
[TestMethod, ExpectedException(typeof(Exception))]
public async Task DoFaultedTaskThrowOnAwait()
{
var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
await task;
}
[TestMethod, ExpectedException(typeof(AggregateException))]
public void DoFaultedTaskThrowOnWait()
{
var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
task.Wait();
}
Both tests pass, notice that Wait throws an AggregateException, and await throws the Exception.
What happens if I await the Result of a Task inside another task?
You can only await a Task.Result if it is an awaitable (meaning it has a GetAwaiter method). This is rarely the case. I assume you mean await on an inner Task.
But what happens if the helper fails? Will the current task remain locked?
First, im not sure what you mean by "locked". The task isn't locked while you await on the inner task. Control is yielded back to the calling method until that inner task completes. If that inner task fails and you fail to properly handle that exception, you parent task will fault as well. You need to make sure you handle exceptions gracefully:
var task =
Task.Run(async () =>
{
try
{
await AsyncHelper.DoSomethingAsync();
}
catch (Exception e)
{
// Handle exception gracefully
}
});
If you await on the parent Task, you'll notice the inner exception propogating from the unhandled inner Task.
Can I avoid this deadlock somehow?
I see no reason why your code should deadlock in this specific case. Not sure why this worries you.
I use a set of tasks at times, and in order to make sure they are all awaited I use this approach:
public async Task ReleaseAsync(params Task[] TaskArray)
{
var tasks = new HashSet<Task>(TaskArray);
while (tasks.Any()) tasks.Remove(await Task.WhenAny(tasks));
}
and then call it like this:
await ReleaseAsync(task1, task2, task3);
//or
await ReleaseAsync(tasks.ToArray());
However, recently I have been noticing some strange behavior and set to see if there was a problem with the ReleaseAsync method. I managed to narrow it down to this simple demo, it runs in linqpad if you include System.Threading.Tasks. It will also work slightly modified in a console app or in an asp.net mvc controller.
async void Main()
{
Task[] TaskArray = new Task[]{run()};
var tasks = new HashSet<Task>(TaskArray);
while (tasks.Any<Task>()) tasks.Remove(await Task.WhenAny(tasks));
}
public async Task<int> run()
{
return await Task.Run(() => {
Console.WriteLine("started");
throw new Exception("broke");
Console.WriteLine("complete");
return 5;
});
}
What I don't understand is why the Exception never shows up anywhere. I would have figured that if the Tasks with the exception were awaited, it would throw. I was able to confirm this by replacing the while loop with a simple for each like this:
foreach( var task in TaskArray )
{
await task;//this will throw the exception properly
}
My question is, why doesn't the shown example throw the exception properly (it never shows up anywhere).
TL;DR: run() throws the exception, but you're awaiting WhenAny(), which doesn't throw an exception itself.
The MSDN documentation for WhenAny states:
The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
Essentially what is happening is that the task returned by WhenAny simply swallows the faulted task. It only cares about the fact that the task is finished, not that it has successfully completed. When you await the task, it simply completes without error, because it is the internal task that has faulted, and not the one you're awaiting.
A Task not being awaited or not using its Wait() or Result() method, will swallow the exception by default. This behavior can be modified back to the way it was done in .NET 4.0 by crashing the running process once the Task was GC'd. You can set it in your app.config as follows:
<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>
A quote from this blog post by the Parallel Programming team in Microsoft:
Those of you familiar with Tasks in .NET 4 will know that the TPL has the notion of “unobserved” exceptions. This is a compromise between two competing design goals in TPL: to support marshaling unhandled exceptions from the asynchronous operation to the code that consumes its completion/output, and to follow standard .NET exception escalation policies for exceptions not handled by the application’s code. Ever since .NET 2.0, exceptions that go unhandled on newly created threads, in ThreadPool work items, and the like all result in the default exception escalation behavior, which is for the process to crash. This is typically desirable, as exceptions indicate something has gone wrong, and crashing helps developers to immediately identify that the application has entered an unreliable state. Ideally, tasks would follow this same behavior. However, tasks are used to represent asynchronous operations with which code later joins, and if those asynchronous operations incur exceptions, those exceptions should be marshaled over to where the joining code is running and consuming the results of the asynchronous operation. That inherently means that TPL needs to backstop these exceptions and hold on to them until such time that they can be thrown again when the consuming code accesses the task. Since that prevents the default escalation policy, .NET 4 applied the notion of “unobserved” exceptions to complement the notion of “unhandled” exceptions. An “unobserved” exception is one that’s stored into the task but then never looked at in any way by the consuming code. There are many ways of observing the exception, including Wait()’ing on the Task, accessing a Task’s Result, looking at the Task’s Exception property, and so on. If code never observes a Task’s exception, then when the Task goes away, the TaskScheduler.UnobservedTaskException gets raised, giving the application one more opportunity to “observe” the exception. And if the exception still remains unobserved, the exception escalation policy is then enabled by the exception going unhandled on the finalizer thread.
From the comment:
these [tasks] were tied to managed resources and I wanted to release them when
they became available instead of waiting for all of them to complete
and then releasing.
Using a helper async void method may give you the desired behavior for both removing the finished tasks from the list and immediately throwing unobserved exceptions:
public static class TaskExt
{
public static async void Observe<TResult>(Task<TResult> task)
{
await task;
}
public static async Task<TResult> WithObservation(Task<TResult> task)
{
try
{
return await task;
}
catch (Exception ex)
{
// Handle ex
// ...
// Or, observe and re-throw
task.Observe(); // do this if you want to throw immediately
throw;
}
}
}
Then your code might look like this (untested):
async void Main()
{
Task[] TaskArray = new Task[] { run().WithObservation() };
var tasks = new HashSet<Task>(TaskArray);
while (tasks.Any<Task>()) tasks.Remove(await Task.WhenAny(tasks));
}
.Observe() will re-throw the task's exception immediately "out-of-band", using SynchronizationContext.Post if the calling thread has a synchronization context, or using ThreadPool.QueueUserWorkItem otherwise. You can handle such "out-of-band" exceptions with AppDomain.CurrentDomain.UnhandledException).
I described this in more details here:
TAP global exception handler