I have a problem in the following code:
static void Main (string[] args)
{
Task newTask = Task.Factory.StartNew(MainTask);
newTask.ContinueWith ((Task someTask) =>
{
Console.WriteLine ("Main State=" + someTask.Status.ToString () + " IsFaulted=" + someTask.IsFaulted+" isComplete="+someTask.IsCompleted);
});
while (true)
{
}
}
static async Task MainTask()
{
Console.WriteLine ("MainStarted!");
Task someTask = Task.Factory.StartNew (() =>
{
Console.WriteLine ("SleepStarted!");
Thread.Sleep(1000);
Console.WriteLine ("SleepEnded!");
});
await someTask;
Console.WriteLine ("Waiting Ended!!");
throw new Exception ("CustomException!");
Console.WriteLine ("NeverReaches here!!");
}
I just want to get Exception from new started task MainTask
. But the result was not what I was expected.
MainStarted!
Main State = RanToCompletion IsFaulted = False isComplete = True
SleepStarted!
SleepEnded!
Waiting Ended!!
As you can see the result, task finishes before "Waiting Ended!!" console log.
I don't have a clue that why MainTask
ended even if in MainTask
has await
command inside?
Did I missed something?
Async void methods can wreak havoc if the caller isn't expecting them to be async. When the return type is Task, the caller knows it's dealing with a future operation; when the return type is void, the caller might assume the method is complete by the time it returns.
If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown. As a best practice, you should always await the call. By default, this message is a warning.
Async methods that don't contain a return statement or that contain a return statement that doesn't return an operand usually have a return type of Task. Such methods return void if they run synchronously.
Async methods are intended to be non-blocking operations. An await expression in an async method doesn't block the current thread while the awaited task is running. Instead, the expression signs up the rest of the method as a continuation and returns control to the caller of the async method.
Task.Factory.StartNew
does not understand async delegates so you need to use Task.Run
in this case and the exception should flow through.
Task.Factory.StartNew(MainTask);
is essentially equivalent to
Task.Factory.StartNew(() => MainTask);
which ignores the returned task from MainTask
and the exception just gets swallowed.
See this blog post for more details.
Try using Task.Run
instead and you'll get your exception:
void Main(string[] args)
{
Task newTask = Task.Run(MainTask);
newTask.ContinueWith((Task someTask) =>
{
Console.WriteLine("Main State=" + someTask.Status.ToString() + " IsFaulted=" + someTask.IsFaulted + " isComplete=" + someTask.IsCompleted);
});
while (true)
{
}
}
static async Task MainTask()
{
Console.WriteLine("MainStarted!");
Task someTask = Task.Run(() =>
{
Console.WriteLine("SleepStarted!");
Thread.Sleep(1000);
Console.WriteLine("SleepEnded!");
});
await someTask;
Console.WriteLine("Waiting Ended!!");
throw new Exception("CustomException!");
Console.WriteLine("NeverReaches here!!");
}
There's great answers here, but I'd like to point out the obvious - the Task.Factory.StartNew
is completely redundant, unnecessary and used wrong.
If you replace
Task newTask = Task.Factory.StartNew(MainTask);
with
Task newTask = MainTask();
You'll get exactly the behaviour you expect, without wasting yet another threadpool thread just to start another threadpool thread. In fact, if you wanted to rewrite your example to be more idiomatic, you'd use something like this:
static void Main (string[] args)
{
var task =
MainTask()
.ContinueWith(t => Console.WriteLine("Main State={0}", t.Status));
task.Wait();
}
static async Task MainTask()
{
Console.WriteLine ("MainStarted!");
await Task.Delay(1000);
Console.WriteLine ("Waiting Ended!!");
throw new Exception ("CustomException!");
Console.WriteLine ("NeverReaches here!!");
}
This code only uses a thread pool thread for the code after the delay, and rethrows the exception on the task.Wait()
call - you might want to do something else, of course.
As a side-note, even if you don't want to explicitly wait for the task to complete, you shouldn't use while (true) {}
to prevent the application from terminating - a simple Console.ReadLine()
will work just as well, and isn't going to push one of your CPU cores to 100% utilization :)
I modified your problem here to catch the exceptions.
static void Main(string[] args)
{
DoFoo();
Console.ReadKey();
}
static async void DoFoo()
{
try
{
await Foo();
}
catch (Exception ex)
{
//This is where you can catch your exception
}
}
static async Task Foo()
{
await MainTask().ContinueWith((Task someTask) =>
{
Console.WriteLine("Main State=" + someTask.Status.ToString() + " IsFaulted=" + someTask.IsFaulted + " isComplete=" + someTask.IsCompleted);
}, TaskContinuationOptions.NotOnFaulted);
}
static async Task MainTask()
{
Console.WriteLine("MainStarted!");
Task someTask = Task.Run(() =>
{
Console.WriteLine("SleepStarted!");
Thread.Sleep(1000);
Console.WriteLine("SleepEnded!");
});
await someTask;
throw new Exception("CustomException!");
Console.WriteLine("Waiting Ended!!");
}
you should use TaskContinuationOptions.NotOnFaulted
which means that the continue with task will only execute if the parent task did not had any exceptions.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With