I encountered an issue when I was using Task.Factory.StartNew
and tried to capture an exception
that is thrown. In my application I have a long running task that I want to encapsulate in a Task.Factory.StartNew(.., TaskCreationOptions.LongRunning);
However, the exception isn't caught when I'm using Task.Factory.StartNew
. It is however working as I expect when I use Task.Run
, which I thought was just a wrapper on Task.Factory.StartNew
(according to for instance this MSDN article).
A working example is provided here, the difference being that the exception is written to console when using Task.Run
, but not when using Factory.StartNew
.
My question would be:
if I have a LongRunning
task that has the possibility to throw exceptions, how should I handle them in the calling code?
private static void Main(string[] args)
{
Task<bool> t = RunLongTask();
t.Wait();
Console.WriteLine(t.Result);
Console.ReadKey();
}
private async static Task<bool> RunLongTask()
{
try
{
await RunTaskAsync();
}
catch (Exception e)
{
Console.WriteLine(e);
return false;
}
Console.WriteLine("success");
return true;
}
private static Task RunTaskAsync()
{
//return Task.Run(async () =>
// {
// throw new Exception("my exception");
// });
return Task.Factory.StartNew(
async () =>
{
throw new Exception("my exception");
});
}
I'll pull some of my comments into an answer because they turned out to be helpful:
LongRunning
is identical to forcing a new thread to be created in practice. And your async method is probably not on that thread for a long time (it is taken off at the first await point). You don't want LongRunning in this case.It does not matter how long the async method runs. The thread is destroyed at the very first await (that operates on a non-completed task).
Can the compiler use this hint in any way? The compiler is generally unable to analyze your code in any major way. Also the compiler does not know anything about the TPL. The TPL is a library. And this library will just always launch a new thread. Specify
LongRunning
iff your task will almost always burn 100% CPU for multiple seconds or will block for multiple seconds with very high probability.My guess is you don't want
LongRunning
here because if you're blocking, why are you using async in the first place? async is about not blocking but getting off the thread.It should be possible when you first Unwrap the task:
Or alternatively:
Your problem is that
StartNew
doesn't work likeTask.Run
withasync
delegates. The return type ofStartNew
isTask<Task>
(which is convertible toTask
). The "outer"Task
represents the beginning of the method, and the "inner"Task
represents the completion of the method (including any exceptions).To get to the inner
Task
, you can useUnwrap
. Or you can just useTask.Run
instead ofStartNew
forasync
code.LongRunning
is just an optimization hint and is really optional. Stephen Toub has a good blog post on the difference betweenStartNew
andRun
and whyRun
is (usually) better forasync
code.Update from @usr comment below:
LongRunning
only applies to the beginning of theasync
method (up until the first incomplete operation isawait
ed). So it's almost certainly better all around to useTask.Run
in this case.