Why exception before await not throw without await? - c#

Consider the following code:
static async Task ThrowException()
{
throw new Exception("exception 1");
Thread.Sleep(1000);
await Task.Delay(1000);
throw new Exception("exception 2");
}
static async Task Sleep()
{
Console.WriteLine("begin sleep");
Thread.Sleep(1000);
Console.WriteLine("end sleep");
Console.WriteLine("begin delay");
await Task.Delay(1000);
Console.WriteLine("end delay");
}
static async Task Main(string[] args)
{
{
Console.WriteLine("begin var task = Sleep();");
var task = Sleep();
Console.WriteLine("end var task = Sleep();");
Console.WriteLine("begin await task;");
await task;
Console.WriteLine("end await task;");
}
{
try
{
Console.WriteLine("begin var task = ThrowException();");
var task = ThrowException();
Console.WriteLine("end var task = ThrowException();");
Console.WriteLine("begin await task;");
await task;
Console.WriteLine("end await task;");
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
}
The result is following:
begin var task = Sleep();
begin sleep
end sleep
begin delay
end var task = Sleep();
begin await task;
end delay
end await task;
begin var task = ThrowException();
end var task = ThrowException();
begin await task;
exception 1
My question is that why exception 1 appears after "begin await task" task but "end sleep" appears before "begin await task"? I think it does not start a new thread before await Task.Delay(), it should happen in the same thread like Thread.Sleep(), so I expect the "exception 1" throw immediately before "end var task = ThrowException();"

This code is
static async Task ThrowException()
{
throw new Exception("exception 1");
Thread.Sleep(1000);
await Task.Delay(1000);
throw new Exception("exception 2");
}
similar to
static Task ThrowException()
{
Task.Run(() =>
{
throw new Exception("exception 1");
Thread.Sleep(1000);
await Task.Delay(1000);
throw new Exception("exception 2");
}
)
}
The trick is the exception is swallowed by the task.
Then the exception is stored in the task and when you ask the task's result, that throw the exception.
And await return the task's result.
In your case :
try
{
Console.WriteLine("begin var task = ThrowException();");
// 1) Display : "begin var task = ThrowException();"
var task = ThrowException();
// 2) Starts a task that immediately ends with an exception
Console.WriteLine("end var task = ThrowException();");
// 3) Display : "end var task = ThrowException();"
Console.WriteLine("begin await task;");
// 4) Display : "begin await task;"
await task;
// 5) await return the task result,
// in this case it's throw the exception stored in the task.
// Go to in catch
Console.WriteLine("end await task;");
var task = ThrowException();
await task;
}
catch(Exception e)
{
Console.WriteLine(e.Message);
//6) Display the exception message
}

According to Microsoft Documentation
The await operator suspends evaluation of the enclosing async method
until the asynchronous operation represented by its operand completes.
When the asynchronous operation completes, the await operator returns
the result of the operation,
*I wont dive deep into how multithreading works, I've tried to use an over-simplified model to make it easy to understand what is going on here. Under the hood it's a little bit more complicated.
In both cases var task = Sleep(); and var task = ThrowException();, instructions within those methods are executed when you assign the Task to the task variable. The difference is that Sleep() method does not have anything to return, so the await here does nothing. On the other hand, ThrowExeption() has something to return. Note that throwing an exeption or returning a value from a function are very similar actions. In this case, the exeption is thrown way before you access it with the await keyword.
It's like sayin: Hey Sleep() and ThrowExeption(), do your work, I'll get to you later when I'll need your results.
*Later:
Hey Sleep(), give me your results
I did what you said, I don't have anything to return to you.
Hey TrowExeption(), give me the results of your work
Alright! I've been waiting for you, here is the exeption that you might be interested in.

There are already answers here, but as I was also confused, just a few words (too long for a comment). There is big difference between:
static async Task ThrowException()
{
throw new Exception("exception 1");
}
and
static Task ThrowException()
{
throw new Exception("exception 1");
}
The first one returns a failed Task, here the exception will be thrown by awaiting the task result.
The second one throws immediately an exception.

I think you are confusing the question.
Because everything in your program is executing in sequencial order.
I think your confusion is that
you think you are executing the task in a asyncronous way so you are expecting
other order maybe.
If you want to execute asyncronously you should do this for example:
Task task = Task.StartNew(Sleep());
Detail your question better please.

Related

Do something periodically while waiting for result of an GetAsync

I want to start an GetAsync or PostAsync and then in a loop do something and check for results.
req1 = client.GetAsync(url_1); // a time consuming request
do
{
//do something here
var req2= await client.GetAsync(url_2);
var result2 = await req2.Content.ReadAsStringAsync();
} while (!IsResultReady(req1)); // check if url_1 job is done and stop the loop
var result1 = await req1.Content.ReadAsStringAsync();
this example should give you what you need
async Task Main()
{
var mainTask = MyLongRunningTask();
// mainTask is already started without await
do
{
await DoSomethingElse();
} while (!mainTask.IsCompleted);
}
public async Task MyLongRunningTask()
{
Console.WriteLine("Long Running Task Started");
await Task.Delay(3000); // simulating client.GetAsync(url_1)
Console.WriteLine("Long Running Task Finished");
}
async Task DoSomethingElse()
{
Console.WriteLine("doing some other tasks");
await Task.Delay(1000);
}
output:
Long Running Task Started
doing some other tasks
doing some other tasks
doing some other tasks
Long Running Task Finished

Does Task.WhenAll wait for all the tasks in case of exceptions

I have two tasks. I run both of them with Task.WhenAll. What happens if one of them throws an exception? Would the other one complete?
Just run this code to test it:
private static async Task TestTaskWhenAll()
{
try
{
await Task.WhenAll(
ShortOperationAsync(),
LongOperationAsync()
);
}
catch (Exception exception)
{
Console.WriteLine(exception.Message); // Short operation exception
Debugger.Break();
}
}
private static async Task ShortOperationAsync()
{
await Task.Delay(1000);
throw new InvalidTimeZoneException("Short operation exception");
}
private static async Task LongOperationAsync()
{
await Task.Delay(5000);
throw new ArgumentException("Long operation exception");
}
Debugger will stop in 5 seconds. Both exceptions are thrown, but Debugger.Break() is hit only once. What is more, the exception value is not AggregateException, but InvalidTimeZoneException. This is because of new async/await which does the unwrapping into the actual exception. You can read more here. If you want to read other Exceptions (not only the first one), you would have to read them from the Task returned from WhenAll method call.
Will the other one complete?
It won't be stopped as a result of the other one failing.
But will it complete?
Task.When will wait for all to complete, whether any or none fail. I just tested with this to verify - and it took 5 seconds to complete:
Task allTasks = Task.WhenAll(getClientToken, getVault, Task.Delay(5000));
If you want to group the tasks you can create a 'new task', then await that.
Task allTasks = Task.WhenAll(getClientToken, getVault, Task.Delay(5000));
try
{
await allTasks;
} catch (Exception ex)
{
// ex is the 'unwrapped' actual exception
// I'm not actually sure if it's the first task to fail, or the first in the list that failed
// Handle all if needed
Exceptions[] allExceptions = allTasks.Exceptions;
// OR
// just get the result from the task / exception
if (getVault.Status == TaskStatus.Faulted)
{
...
}
}
Had the same question and tested by myself. In short:
It always wait for all tasks to finish.
The first exception is thrown if there is any after all tasks are finished (crash if you don't catch).
For all exceptions, keep the Task instance returned by Task.WhenAll and use Exception.InnerExceptions property.
Here's my test:
static async Task Main(string[] args)
{
var tasks = new[] { Foo1(), Foo2(), Foo3() };
Task t = null;
try
{
t = Task.WhenAll(tasks);
await t;
}
catch (Exception ex)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
Console.WriteLine("All have run.");
if (t.Exception != null)
{
foreach (var ex in t.Exception.InnerExceptions)
{
Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
}
}
static async Task Foo1()
{
await Task.Delay(50);
throw new ArgumentException("zzz");
}
static async Task Foo2()
{
await Task.Delay(1000);
Console.WriteLine("Foo 2");
throw new FieldAccessException("xxx");
}
static async Task Foo3()
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(200);
Console.WriteLine("Foo 3");
}
}
Output:
Foo 3
Foo 3
Foo 3
Foo 3
Foo 2
Foo 3
Foo 3
Foo 3
Foo 3
Foo 3
Foo 3
ArgumentException: zzz
All have run.
ArgumentException: zzz
FieldAccessException: xxx

CancellationToken is not cancelling all tasks

Newbie to TPL in .NET. Trying to understand CancellationToken and how they are signaled to cancel an executing Task. The below code only transmits one Task being cancelled where as same token is passed to both Task. My assumption is if the timeout happen on first task and it execute ctx.Cancel() I need a little help in understanding why I am only seeing one exception where as both task should be cancelled. What am I missing and how do I ensure both Tasks are cancelled and not taking memory resources.
static void Main(string[] args)
{
Console.WriteLine("Starting application");
var ctx = new CancellationTokenSource();
var token = ctx.Token;
try
{
var task1 = new Program().Run("task1", token);
var task2 = new Program().Run("task2", token);
if (!task1.Wait(1000))
ctx.Cancel();
task2.Wait();
}
catch (AggregateException ex)
{
Console.WriteLine("Aggregate Exception occurred");
foreach (var e in ex.InnerExceptions)
{
Console.WriteLine(e.Message);
}
}
catch (Exception e)
{
Console.WriteLine($"Main Exception: {e.Message}");
}
finally
{
Console.WriteLine("Finish Application");
ctx.Dispose();
}
}
private async Task Run(string name, CancellationToken token)
{
while(true)
{
if (token.IsCancellationRequested)
{
Console.WriteLine("Task Cancelled");
token.ThrowIfCancellationRequested();
}
Console.WriteLine($"Executing {name} ...");
await Task.Delay(250, token);
}
}
Only one exception is thrown, what happened to other task? Also, Console.WriteLine("Task Cancelled") never got executed.
Output:
Starting application
Executing task1 ...
Executing task2 ...
Executing task2 ...
Executing task1 ...
Executing task1 ...
Executing task2 ...
Executing task2 ...
Executing task1 ...
Aggregate Exception occurred
A task was canceled.
Finish Application
Two things:
You should call ex.Flatten().InnerExceptions take a look at this for reference
The Task.Delay is throwing instead of your cancellation logic. Try wrapping that in a try catch for logging. Alternatively, you could just not pass the token to Task.Delay.

How to cancel Task but wait until it finishes?

I have threaded task wich performs some operation in loop:
static void TaskAction(CancellationToken ct)
{
while (SomeCondition())
{
DoSomeSeriousJob();
ct.ThrowIfCancellationRequested();
}
}
static void DoSomeSeriousJob()
{
Console.WriteLine("Serious job started");
Thread.Sleep(5000);
Console.WriteLine("Serious job done");
}
I start it and then cancel after some period of time:
var cts = new CancellationTokenSource();
var task = Task.Factory.StartNew(() => TaskAction(cts.Token), cts.Token);
Thread.Sleep(1000);
cts.Cancel();
This operation must be finished correctly, I don't want to interrupt it. But I want to send a cancellation request to my task and wait until it finishes correctly (by which I mean it gets to some point in code).
I tryed following approaches:
1. Wait(CancellationToken ct)
try
{
task.Wait(cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Task cancelled");
}
// Must be joined here.
In this case program returns immediately from Wait(). The task continues to run until ThrowIfCancellationRequested() but if main thread exits the task gets interrupted too.
2. Wait()
try
{
task.Wait();
}
catch (OperationCanceledException)
{
Console.WriteLine("Task cancelled");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Here main thread waits for completion but at the end AggregateException is risen with InnerException = TaskCancelledException (not OperationCancelledException).
3. Check IsCancellationRequested() and no exceptions
static void TaskAction(CancellationToken ct)
{
while (SomeCondition())
{
DoSomeSeriousJob();
if (ct.IsCancellationRequested)
break;
}
}
// ...
task.Wait();
In this case no exceptions are risen but the task gets status RanToCompletion in the end. This is not distiguishable from correct completion when SomeCodition() starts to return false.
All these problem have easy workarounds but I wonder, may be I'm missing something? Could anybody advise me better solution?
If you want to wait for the task to complete (or gets cancelled) synchronously, you can try this:
cts.Cancel();
Task.Run(async () => {
try {
await task;
}
catch (OperationCanceledException ex) {
// ...
}
).Wait();
So that you can directly catch OperationCanceledException instead of catching an AggregateException.
Edit:
Wait(CanecllationToken)
This approach won't work for that purpose.
MSDN statement:
Waits for the Task to complete execution. The wait terminates if a cancellation token is canceled before the task completes.
Wait()
You can use this approach but as you can see, you should expect an AggregateException not OperationCanceledException. It is also specified in documents of the method.
The AggregateException.InnerExceptions collection contains a TaskCanceledException object.
So in this approach, in order to make sure operation is cancelled, you can check if inner expection contains a TaskCanceledException or not.
Check IsCancellationRequested() and no exceptions
In this way, this is obvious that no exception is thrown and you can't find out if the operation is cancelled or not.
If you don't want to wait synchronously, everything works as expected:
cts.Cancel();
try {
await task;
}
catch (OperationCanceledException ex) {
// ...
}
Try this:
try
{
task.GetAwaiter().GetResult();
}
catch (OperationCanceledException)
{
Console.WriteLine("Task cancelled");
}
You'll get an OperationCanceledException and it won't be wrapped with AggregateException.

Using CancellationToken for timeout in Task.Run does not work [duplicate]

This question already has answers here:
How to cancel a Task in await?
(4 answers)
Closed 3 years ago.
OK, my questions is really simple. Why this code does not throw TaskCancelledException?
static void Main()
{
var v = Task.Run(() =>
{
Thread.Sleep(1000);
return 10;
}, new CancellationTokenSource(500).Token).Result;
Console.WriteLine(v); // this outputs 10 - instead of throwing error.
Console.Read();
}
But this one works
static void Main()
{
var v = Task.Run(() =>
{
Thread.Sleep(1000);
return 10;
}, new CancellationToken(true).Token).Result;
Console.WriteLine(v); // this one throws
Console.Read();
}
Cancellation in Managed Threads:
Cancellation is cooperative and is not forced on the listener. The listener determines how to gracefully terminate in response to a cancellation request.
You didn't write any code inside your Task.Run method to access your CancellationToken and to implement cancellation - so you effectively ignored the request for cancellation and ran to completion.
There is a difference in canceling a running task, and a task scheduled to run.
After the call to the Task.Run method, the task is only scheduled, and probably have not been executed yet.
When you use the Task.Run(..., CancellationToken) family of overloads with cancellation support, the cancellation token is checked when the task is about to run. If the cancellation token has IsCancellationRequested set to true at this time, an exception of the type TaskCanceledException is thrown.
If the task is already running, it is the task's responsibility to call the ThrowIfCancellationRequested method, or just throw the OperationCanceledException.
According to MSDN, it's just a convenience method for the following:
if (token.IsCancellationRequested)
throw new OperationCanceledException(token);
Note the different kind of exception used in this two cases:
catch (TaskCanceledException ex)
{
// Task was canceled before running.
}
catch (OperationCanceledException ex)
{
// Task was canceled while running.
}
Also note that TaskCanceledException derives from OperationCanceledException, so you can just have one catch clause for the OperationCanceledException type:
catch (OperationCanceledException ex)
{
if (ex is TaskCanceledException)
// Task was canceled before running.
// Task was canceled while running.
}
In the first variant of your code, you're not doing anything to manage the cancellation token.
For example, you're not checking whether token.IsCancellationRequested returns true (and then throwing an exception) or calling ThrowIfCancellationRequested() from your CancellationToken object.
Also, the Task.Run overload you used checks whether the token is already canceled or not when the task is about to start and your code states that token will report cancellation after 500 ms.
So, your code is just ignoring the cancellation request and that's why the task ran to completion.
You should do something like this:
void Main()
{
var ct = new CancellationTokenSource(500).Token;
var v =
Task.Run(() =>
{
Thread.Sleep(1000);
ct.ThrowIfCancellationRequested();
return 10;
}, ct).Result;
Console.WriteLine(v); //now a TaskCanceledException is thrown.
Console.Read();
}
or this, without passing the token, as others already noted:
void Main()
{
var ct = new CancellationTokenSource(500).Token;
ct.ThrowIfCancellationRequested();
var v =
Task.Run(() =>
{
Thread.Sleep(1000);
return 10;
}).Result;
Console.WriteLine(v); //now a TaskCanceledException is thrown.
Console.Read();
}
The second variant of your code works, because you're already initializing a token with a Canceled state set to true. Indeed, as stated here:
If canceled is true, both CanBeCanceled and IsCancellationRequested will be true
the cancellation has already been requested and then the exception TaskCanceledException will be immediately thrown, without actually starting the task.
Another implementation using Task.Delay with token instead it Thread.Sleep.
static void Main(string[] args)
{
var task = GetValueWithTimeout(1000);
Console.WriteLine(task.Result);
Console.ReadLine();
}
static async Task<int> GetValueWithTimeout(int milliseconds)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
cts.CancelAfter(milliseconds);
token.ThrowIfCancellationRequested();
var workerTask = Task.Run(async () =>
{
await Task.Delay(3500, token);
return 10;
}, token);
try
{
return await workerTask;
}
catch (OperationCanceledException )
{
return 0;
}
}

Categories