Task.Run vs Task.Factory.StartNew - Expected Deadlock is not happening - c#

I read about the differences of Task.Run and Task.Factory.StartNew.
Task.Run(() => {});
should be equivalent to
Task.Factory.StartNew(() => {}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
But in my code I expect a deadlock which is not happening because of Task.Factory.StartNew:
private Task backgroundTask;
private async Task DoSomethingAsync()
{
// this should deadlock
await this.backgroundTask.ConfigureAwait(false);
throw new Exception();
}
private async Task Test()
{
this.backgroundTask = Task.Factory.StartNew(async () =>
{
await this.DoSomethingAsync().ConfigureAwait(false);
}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
// just wait here for testing/debugging
await Task.Delay(10000).ConfigureAwait(false);
// if no deadlock, this should throw
await this.backgroundTask.ConfigureAwait(false);
}
But it is not deadlocking. The exception in DoSomethingAsync is thrown but never catched.
Awaiting the Task after the Task.Delay is not throwing either, because it is RanToCompletion.
When using Task.Run it is deadlocking as expected:
private Task backgroundTask;
private async Task DoSomethingAsync()
{
// this is deadlocking
await this.backgroundTask.ConfigureAwait(false);
throw new Exception();
}
private async Task Test()
{
this.backgroundTask= Task.Run(async () =>
{
await this.DoSomethingAsync().ConfigureAwait(false);
});
// just wait here for testing/debugging
await Task.Delay(10000).ConfigureAwait(false);
// never reached because of deadlock
await this.backgroundTask.ConfigureAwait(false);
}
Can anybody explain this behaviour?

The method Task.Factory.StartNew when used with an async delegate returns a nested task: Task<Task>. You are assigning this nested task to a variable of type Task, which is allowed because the class Task<TResult> derives from the class Task. What happens then is that you lose the reference to the inner task, so you have no way of awaiting it. When you await this.backgroundTask you are awaiting the outer Task<Task>, but you can't get the result of the await operation, which is a Task, the inner Task, and await it. The inner task has become a fire-and-forget task.

Related

Similar code in Tasks returning different status codes

I am throwing an OperationCanceledException from three different Tasks and each having slight differences, as per the code below:
static async Task ThrowCancellationException()
{
throw new OperationCanceledException();
}
static void Main(string[] args)
{
var t1 = new Task(() => throw new OperationCanceledException());
t1.Start();
try { t1.Wait(); } catch { }
Task t2 = new Task(async () => throw new OperationCanceledException());
t2.Start();
try { t2.Wait(); } catch { }
Task t3 = ThrowCancellationException();
Console.WriteLine(t1.Status); // prints Faulted
Console.WriteLine(t2.Status); // prints RanToCompletion
Console.WriteLine(t3.Status); // prints Canceled
}
My question is:
Why the statuses are different for each task?
I could understand that there are differences between the code/lambdas marked with async and the lambda not marked with async but the status is different even between the async lambda and the async method running the same code.
I could understand that there are differences between the code/lambdas marked with async and the lambda not marked with async but the status is different even between the async lambda and the async method running the same code.
This isn't quite true.
If you look carefully at that new Task(async () => throw new OperationCanceledException()), you'll see it's calling the overload new Task(Action action) (there's no overload which takes a Func<Task>). This means that it's equivalent to passing an async void method, not an async Task method.
So:
Task t2 = new Task(async () => throw new OperationCanceledException());
t2.Start();
try { t2.Wait(); } catch { }
This compiles to something like:
private static async void CompilerGeneratedMethod()
{
throw new OperationCanceledException()
}
...
Task t2 = new Task(CompilerGeneratedMethod);
t2.Start();
try { t2.Wait(); } catch { }
This grabs a thread from the ThreadPool, and runs CompilerGeneratedMethod on it. When an exception is throw from inside an async void method, the exception is re-thrown somewhere appropriate (in this case, it's re-thrown on the ThreadPool), but the CompilerGeneratedMethod method itself returns straight away. This causes the Task t2 to complete straight away, which is why its status is RanToCompletion.
So what happened to the exception? It's about to bring down your application! Stick a Console.ReadLine at the end of your Main, and see that the application exits before you have a chance to press enter.
This:
Task t3 = ThrowCancellationException();
is very different. It's not attempting to run anything on the ThreadPool. ThrowCancellationException is run synchronously, and synchronously returns a Task which contains the OperationCanceledException. A Task which contains an OperationCanceledException is treated as Canceled.
If you want to run an async method on the ThreadPool, use Task.Run. This has an overload which takes a Func<Task>, which means that:
Task t2 = Task.Run(async () => throw new OperationCanceledException());
Compiles to something like:
private static async Task CompilerGeneratedMethod()
{
throw new OperationCanceledException();
}
...
Task t2 = Task.Run(CompilerGeneratedMethod);
Here, when CompilerGeneratedMethod is executed on the ThreadPool, it returns a Task containing the OperationCanceledException. The Task machinery then transitions the Task t2 to the Canceled state.
As an aside, avoid new Task, and prefer using Task.Run if you want to explicitly run a method on the ThreadPool. There are lots of methods in the TPL which were introduced before async/await, and are confusing when used with it.
When you create a task using the Task constructor, you can provide a CancellationToken as optional argument. The task will result in a Canceled state only in case of an OperationCanceledException that is associated with this specific CancellationToken. Otherwise the exception will be interpreted as a fault. Here is how you can associate an OperationCanceledException with a CancellationToken:
var cts = new CancellationTokenSource();
var t1 = new Task(() =>
{
cts.Cancel();
throw new OperationCanceledException(cts.Token);
}, cts.Token);
About using the Task constructor with an async delegate as argument, the correct way to do it is by creating a nested Task<Task>:
Task<Task> t2 = new Task<Task>(async () => throw new OperationCanceledException());
t2.Start();
try { t2.Result.Wait(); } catch { }
Console.WriteLine(t2.Result.Status); // prints Canceled

Why doesn't it work to use ContinueWith to run task sequentially?

My goal was to start "Task2" after "Task1". At first I wrote code like "Code1" below, but it did not work (Task2 started before Task1 was finished). So I searched Stackoverflow and modified my code like "Code2" below as the existing answer suggested. I wonder why "Code1" did not work.
Code1
static void Main(string[] args)
{
var p = new Program();
p.Test2();
Console.ReadKey();
}
void Test2()
{
Task.Factory.StartNew(async () =>
{
await Task1();
}).ContinueWith((t) => {
Task2();
});
}
async Task Task1()
{
Debug.WriteLine("Task 1 starting....");
await LongTask();
Debug.WriteLine("Task 1 done");
}
Task LongTask()
{
return Task.Factory.StartNew(() =>
{
Thread.Sleep(3000);
});
}
void Task2()
{
Debug.WriteLine("Task 2");
}
Code2
Task.Factory.StartNew(async () =>
{
await Task1();
Task2();
}).ContinueWith((t) => {
//Task2();
});
Because when you are running task like Task.Factory.StartNew(async () => ... it returns Task<Task> (task of task). And first task terminates without waiting for the inner Task.
For preventing this situation, you can use Unwrap method:
Task.Factory.StartNew(async () =>
{
await Task1();
})
.Unwrap()
.ContinueWith((t) => {
Task2();
});
That StartNew will return Task<Task>, and it seems you want to execute continuation after inner task completion. That's why we need Unwrap.
It “unwraps” the inner task that’s returned as the result of the outer task. Calling Unwrap on a Task gives you back a new Task (which we often refer to as a proxy) which represents the eventual completion of the inner task. And then, we are adding continuation to the inner task.
But, as Task.Run will do that unwrapping automatically you can use Task.Run in that case:
Task.Run(async () =>
{
await Task1();
})
.ContinueWith((t) => {
Task2();
});
And that can be simplified to:
Task.Run(async () =>
{
await Task1();
Task2();
});
Detailed information about diferences between Task.Run and Task.Factory.StartNew:
Read more here from Stephen Toub who is an engineer in the .Net team. The reason of decision in case of Task.Run:
Because we expect it to be so common for folks to want to offload work
to the ThreadPool, and for that work to use async/await, we decided to
build this unwrapping functionality into Task.Run.
By the way, as Stephen recommended just try to use Task.Run in most cases, but this in no way obsoletes Task.Factory.StartNew, there is still some places in which Task.Factory.StartNew must be used:
Task.Factory.StartNew still has many important (albeit more advanced)
uses. You get to control TaskCreationOptions for how the task
behaves. You get to control the scheduler for where the task should
be queued to and run. You get to use overloads that accept object
state, which for performance-sensitive code paths can be used to avoid
closures and the corresponding allocations. For the simple cases,
though, Task.Run is your friend.
Task.Factory.StartNew don't understand async lambdas. For Task.Factory.StartNew your lambda is just function returning Task. It doesn't automatically await that task. Also, note that using Task.Factory.StarNew and ContinueWith is discouraged in modern C# as it hard to use correctly (please read, "StartNew is Dangerous" by Stephen Cleary). You should use Task.Factory.StarNew or ContinueWith only if you can't do without them.
Instead, you can just use async/await one more time:
async Task Test2()
{
await Task1();
Task2();
}
or Task.Run:
async Task Test2()
{
return Task.Run(await () =>
{
await Task1();
Task2();
});
}
The second approach might be convenient if you want to make sure that your asynchronous Task1() is started on background thread.

How to wait for later started task

In my code sample main thread doesn't wait when task2 is finished.
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
Console.WriteLine("Main 2");
var task2 = getTask2();
await Task.Delay(1);
Console.WriteLine("Main 3");
task2.Start();
await task2;
Console.WriteLine("Main end");
}
private async Task getTask1()
{
Console.WriteLine("task1 start");
await Task.Delay(100);
Console.WriteLine("task1 end");
}
private Task getTask2()
{
return new Task(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
Result of execution this code is
Main start
task1 start
task1 end
Main 2
Main 3
task2 start
Main end
task2 end
How can I change code where 'Main end' will be at the end of list.
In getTask2 you're starting a Task that just starts another Task. So when you await that Task it will continue executing as soon as it has finished starting the inner task, not when that inner task has finished.
In this case there's no reason at all for you to create a new task just to start a new task in the first place; you should just use the approach used in the first method.
Of course, if, for some reason, you do need to create a new Task just to start a new task then you need to get the inner Task out and ensure that the main method only continues executing when that inner Task has finished. There is an Unwrap method to create a Task that is logically equivalent to a Task that is the Result of another Task:
private Task getTask2()
{
Task<Task> task = new Task<Task>(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
task.Start();
return task.Unwrap();
}
But you don't even need to do that. Task.Run will automatically unwrap the value for you if it is given a lambda that produces a Task:
private Task getTask2()
{
return Task.Run(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
And once again, this is all assuming that just starting the asynchronous operation needs to be offloaded to another thread, which should be very rare. Usually this means that there's a bug in that asynchronous method in that it shouldn't be doing long running synchronous work before giving you its Task in the first place, but on occasion that method will be from an external library that you don't control, so this would be your only option.
The constructor overload for Task that you are using has the following signature:
public Task(Action action)
Although you can assign the following expression to a variable of type Action:
async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
}
this action will point to a synchronous function that will synchronously finish once the await in await Task.Delay(100); is reached (although the rest of the "action" will continue asynchronously). So, as far as the Task object is concerned, the execution of this Action is completed once this await is reached. This means that the await in await task2; will asynchronously continue at that point which explains why "Main end" is printed before "task2 end".
So basically, the constructor for Task does not support asynchronous actions, just synchronous ones.
To fix this problem, you need to start a task using a method that understands asynchronous actions. An asynchronous action looks like this: Func<Task>.
The Task.Run method supports asynchronous actions because it has the following overload:
public static Task Run(Func<Task> function)
So to fix your code, change return new Task(... to return Task.Run(... and then don't call the Start method on the returned task since Task.Run will create a Task that is already started.
If you want to delay the execution of the second "task", then I suggest to make the getTask2 method return Func<Task> (an asynchronous action) instead of Task like this:
private Func<Task> getTask2()
{
return async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
};
}
And then run such asynchronous action when you want from the Run method like this:
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
Console.WriteLine("Main 2");
var task2 = getTask2(); //construct asynchronous action
Console.WriteLine("Main 3");
await Task.Run(task2); //execute the asynchronous action
Console.WriteLine("Main end");
}
new Task( does not work with async functions by itself. You are creating a Task<Task>, you need to call Unwrap() before you await the inner task.
private Task<Task> getTask2()
{
return new Task(async () =>
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
});
}
var task2 = getTask2();
Console.WriteLine("Main 3");
task2.Start();
await task2.Unwrap();
You have an error in your code. You have not marked the getTask2 method with the keyword async yet you await its result. Even though you are using an async delegate within the method you must use the async keyword if it is to be successfully awaited.
Here's an idea as to how you can accomplish your desired result.
public async Task Run()
{
Console.WriteLine("Main start");
await getTask1();
await getTask2();
Console.WriteLine("Main end");
}
public async Task AlternateRun()
{
Console.WriteLine("Main start");
List<Task> task_list = new List<Task>();
task_list.Add(getTask1());
task_list.Add(getTask2());
var task_result = Task.WhenAll(task_list);
task_result.Wait();
Console.WriteLine("Main end");
}
private async Task getTask1()
{
Console.WriteLine("task1 start");
await Task.Delay(100);
Console.WriteLine("task1 end");
}
private async Task getTask2()
{
Console.WriteLine("task2 start");
await Task.Delay(100);
Console.WriteLine("task2 end");
}
I have also added an AlternateRun method that will demonstrate an alternative style of awaiting multiple tasks. If you need your tasks to be completed in order then consider using the ContinueWith extension method.
Notice that I also removed your task2.Start()statement. This is already implied by the await keyword.
Hope this helps.

Catch and propagate exceptions in async methods

I have the following code in a while loop:
Task worker =
Task.Factory
.StartNew(() =>
{
ProduceAsync(param, tokenSource.Token);
}, tokenSource.Token)
.ContinueWith((t) =>
{
OnException(t);
}, TaskContinuationOptions.OnlyOnFaulted);
ProduceAsync looks like this:
private async Task ProduceAsync( List<string> param, CancellationToken token)
{
token.ThrowIfCancellationRequested();
var task = _requestManager.GetProductsAsync(param);
var products;
try
{
products = await task;
_products.Add(products);
}
catch
{
task.GetAwaiter().GetResult();
}
}
When there's an exception inside the async request I would like the program to fail fast, so on OnException(t) I'm cancelling the tasks using tokenSource. The problem here that ProduceAsync does not propagate the exception and OnException isn't being called. How can I get it to propagate the exception?
Note: I copied the code by hand to my phone so there might be compilation errors...
I'm reasonably sure that instead of
Task.Factory.StartNew(() => {
ProduceAsync(param, tokenSource.Token);
}, tokenSource.Token)
.ContinueWith(...
you should be doing:
Task.Run(async () => {
await ProduceAsync(param, tokenSource.Token);
}, tokenSource.Token)
.ContinueWith(...
basically if you don't wait for your async method to complete then it will run separately and the delegate you pass to Task.Factory.StartNew will return immediately. Also you should use Task.Run as its a bit safer.
Also, you may want to do a rethrow with throw in your catch block after you do whatever you want with the exception.

Why is my method not running asynchronously?

I'm running a test that should be calling a method multiple times without waiting for its result. Here's my code:
private async Task HandleJob(string params) {
// do stuff that takes a minute
Thread.Sleep(10000);
return;
}
[TestMethod]
public async Task StartTest() {
HandleJob("one");
HandleJob("two");
}
When I set a break at HandleJob("two"), it only gets hit after one has been completed. How do I make them run asynchronously, so no waiting is done?
You haven't used await in the method at all. This means that the method will execute the lines synchronously. To actually yield back to the caller and let the remaining code run asynchronously, you need to use await, which will continue the method as a continuation on the targeted Task (as well as unboxing any exceptions and return values)
There are a couple of helper methods in Task to assist with this - my favorite is Task.Yield(), which will immediately return, thus an await Task.Yield() will spit out into a new thread and return all at once.
[TestMethod]
public async Task StartTest() {
await Task.Yield(); // await yields control, and Yield will ensure the rest of the method starts immediately
HandleJob("one");
HandleJob("two");
}
If you want the individual subtasks to execute all at once, conglomerate them using Task.WhenAll and Task.Run
[TestMethod]
public async Task StartTest() {
await Task.WhenAll(
Task.Run(() => HandleJob("one")),
Task.Run(() => HandleJob("two")));
}
Your asynchronous method HandleJob is not really async as Thread.Sleep blocks the current thread. Try using await Task.Delay instead. Note that params is a keyword and your original code won't compile. I've changed it to parameters.
private async Task HandleJob(string parameters) {
// do stuff that takes a minute
await Task.Delay(10000);
return;
}
In your start method you can return a single Task that gets completed when both methods are finished using Task.WhenAll as pointed out in one of the other answers. If you return the result rather than await it, then the caller of StartTest can determine whether he waits for the Task to complete or if he ignores it and it becomes a fire and forget situation. Because you are not using await StartTest will not need to be marked as async anymore.
[TestMethod]
public Task StartTest() {
return Task.WhenAll(HandleJob("one"), HandleJob("two"));
}
you need to await both the tasks:
private void HandleJob( params string[] value )
{
return;
}
[TestMethod]
public async Task StartTest()
{
var task1 = Task.Run( () => HandleJob( "one" ) );
var task2 = Task.Run( () => HandleJob( "two" ) );
await Task.WhenAll( task1 , task2 );
}

Categories