How to wait for later started task - c#

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.

Related

In C# an async task waits to finish another async task before it executes

I have this test methods,
private async Task<int> TestOnly()
{
await Task.Delay(10000);
using (StreamWriter sw = File.AppendText(#"asyncTest.txt"))
{
sw.WriteLine($"2 Written TestOnly().");
}
return 100;
}
private async Task TestAgain()
{
await Task.Delay(0);
using (StreamWriter sw = File.AppendText(#"syncTest.txt"))
{
sw.WriteLine($"1 Written here TestAgain().");
}
}
Private async Task Refresh()
{
await TestOnly();
await TestAgain();
}
If I call the Refresh(), what I am expecting that in syncTest.txt, the text 1 Written here TestAgain(). will print first than 2 Written TestOnly(). because the TestOnly() method awaiting 10 seconds before it writes into the asyncTest.txt. But when I run, it waits for it. Why is that?
When you await a task, you cause execution of the current method to suspend until the task is completed. Don't await a task until you're ready to wait for it.
private async Task Refresh()
{
var task1 = TestOnly();
var task2 = TestAgain();
await Task.WhenAll(task1, task2);
}
You can also accomplish the whole thing without async or await. If you can see why, I think it will help you understand what await does.
private Task Refresh()
{
var task1 = TestOnly();
var task2 = TestAgain();
return Task.WaitAll(task1, task2);
}
This may also help: How do yield and await implement control of flow in .NET?

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

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.

Why use async await if same can be achieved by Task.run

Do both methods below behave the same way? Is there an internal difference as to how they work? Both methods will release the UI thread and continue executing once the delay is over.
public async Task DoSomethingAsync()
{
Console.WriteLine("Test Async start");
//Any long running operation
await Task.Delay(5000);
Console.WriteLine("Test Async end");
}
public void TestTask()
{
Console.WriteLine("Test Task start");
Task.Run(() =>
{
//Any long running operation
Thread.Sleep(10000);
}
).ContinueWith((prevTask) =>
{
Console.WriteLine("Test Task end");
});
}
**output**:
Test Async start
Test Task start
Test Async end
Test Task end
Asynchronous programming with async and await
The async and await keywords don't cause additional threads to be
created. Async methods don't require multithreading because an async
method doesn't run on its own thread. The method runs on the current
synchronization context and uses time on the thread only when the
method is active.
You don't need System.Threading for Tasks. The code below does because of CancellationToken.
One difference is with Task you have access to properties on the main (UI) thread.
See Quantity. You can even write to the main thread but probably not a good practice.
async Task<int> TaskDelayAsync(CancellationToken ct, IProgress<int> progress)
{
Debug.WriteLine(Quantity);
int i = 0;
while (true)
{
i++;
//Debug.WriteLine(i);
progress.Report(i);
ct.ThrowIfCancellationRequested();
await Task.Delay(500);
}
return i;
}

How to add a task to the collection that Task.WhenAll is waiting for?

Isn't it possible to add a task to the list that Task.WhenAll is waiting already?
I want to wait for all the tasks to finish, and there is a possibility to create new tasks after initialization. But here Task.WhenAll doesn't wait for the new task that is added to the collection later.
List<Task> tasks = new List<Task>();
var task1 = Task.Run(async () =>
{
Debug.WriteLine("task #1 started");
await Task.Delay(TimeSpan.FromSeconds(20));
Debug.WriteLine("task #1 finished");
});
var task2 = Task.Run(async () =>
{
Debug.WriteLine("task #2 started");
await Task.Delay(TimeSpan.FromSeconds(30));
Debug.WriteLine("task #2 finished");
});
var task3 = Task.Run(async () =>
{
Debug.WriteLine("task #3 started");
await Task.Delay(TimeSpan.FromSeconds(10));
var inner = Task.Run(async () =>
{
Debug.WriteLine("inner task started");
await Task.Delay(TimeSpan.FromSeconds(40));
Debug.WriteLine("inner task finished");
});
tasks.Add(inner);
Debug.WriteLine("task #3 finished");
});
tasks.Add(task1);
tasks.Add(task2);
tasks.Add(task3);
await Task.WhenAll(tasks);
Debug.WriteLine("All finished");
Output:
task #2 started
task #3 started
task #1 started
task #3 finished
inner task started
task #1 finished
task #2 finished
All finished
inner task finished < didn't wait for this to finish
This is because tasks.Add(inner) is executed after await Task.WhenAll(tasks). To answer your question, first you need to clearify the releationship between task #3 and task inner. IMO the inner task should be part of task #3. That is, task #3 can't finish until the inner task finishes.
await inner; //instead of tasks.Add(inner);
Task.WhenAll always copies the references to tasks before it actually starts waiting. This does mean that the original collection can be updated whilst the wait is in progress, but WhenAll is completely oblivious to these changes.
What I'd suggest, instead, is to run your WhenAll in a loop. Something along the lines of:
while(tasks.Count > 0) {
await Task.WhenAll(tasks);
tasks = tasks.Where(t => !t.IsCompleted);
}
(With exact details of whether you wish to leave tasks itself unmodified, what the exact correct data type is for that variable, etc, left as exercises to be filled in by the reader)

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