Serializing async task and async continuation - c#

I have long running processing that I want to perform in a background task. At the end of the task, I want to signal that it has completed. So essentially I have two async tasks that I want to run in the background, one after the other.
I am doing this with continuations, but the continuation is starting prior to the initial task completing. The expected behavior is that the continuation run only after the initial task has completed.
Here is some sample code that demonstrates the problem:
// Setup task and continuation
var task = new Task(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
});
task.Start();
The DebugLog function:
static void DebugLog(string s, params object[] args)
{
string tmp = string.Format(s, args);
System.Diagnostics.Debug.WriteLine("{0}: {1}", DateTime.Now.ToString("HH:mm:ss.ffff"), tmp);
}
Expected Output:
TASK Starting
TASK Finishing
CONTINUATION Starting
CONTINUATION Ending
Actual Output:
TASK Starting
CONTINUATION Starting
CONTINUATION Ending
TASK Finishing
Again, my question is why is the continuation starting prior to the completion of the initial task? How do I get the continuation to run only after the completion of the first task?
Workaround #1
I can make the above code work as expected if I make the intial task synchronous - that is if I Wait on the Task.Delay like so:
var task = new Task(() =>
{
DebugLog("TASK Starting");
Task.Delay(1000).Wait(); // Wait instead of await
DebugLog("TASK Finishing");
});
For many reasons, it is bad to use Wait like this. One reason is that it blocks the thread, and this is something I want to avoid.
Workaround #2
If I take the task creation and move it into it's own function, that seems to work as well:
// START task and setup continuation
var task = Test1();
task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
});
static public async Task Test1()
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
}
Credit for the above approach goes to this somewhat related (but not duplicate) question: Use an async callback with Task.ContinueWith
Workaround #2 is better than Workaround #1 and is likely the approach I'll take if there is no explanation for why my initial code above does not work.

Your original code is not working because the async lambda is being translated into an async void method underneath, which has no built-in way to notify any other code that it has completed. So, what you're seeing is the difference between async void and async Task. This is one of the reasons that you should avoid async void.
That said, if it's possible to do with your code, use Task.Run instead of the Task constructor and Start; and use await rather than ContinueWith:
var task = Task.Run(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
await task;
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
Task.Run and await work more naturally with async code.

You shouldn't be using the Task constructor directly to start tasks, especially when starting async tasks. If you want to offload work to be executed in the background use Task.Run instead:
var task = Task.Run(async () =>
{
DebugLog("TASK Starting");
await Task.Delay(1000); // actual work here
DebugLog("TASK Finishing");
});
About the continuation, it would be better to just append it's logic to the end of the lambda expression. But if you're adamant on using ContinueWith you need to use Unwrap to get the actual async task and store the it so you could handle exceptions:
task = task.ContinueWith(async (x) =>
{
DebugLog("CONTINUATION Starting");
await Task.Delay(100); // actual work here
DebugLog("CONTINUATION Ending");
}).Unwrap();
try
{
await task;
}
catch
{
// handle exceptions
}

Change your code to
// Setup task and continuation
var t1 = new Task(() =>
{
Console.WriteLine("TASK Starting");
Task.Delay(1000).Wait(); // actual work here
Console.WriteLine("TASK Finishing");
});
var t2 = t1.ContinueWith((x) =>
{
Console.WriteLine("C1");
Task.Delay(100).Wait(); // actual work here
Console.WriteLine("C2");
});
t1.Start();
// Exception will be swallow without the line below
await Task.WhenAll(t1, t2);

Related

Why does the synchronous console Main Method hangs calling an async method? [duplicate]

This question already has answers here:
Why this task hangs?
(2 answers)
Execution of tasks hangs with Task.WaitAll in C#
(2 answers)
Closed 1 year ago.
I am failing to understand why calling a async method from the Main method from a console application hangs forever. I feel I am missing some key element of asynchronous execution:
static void Main(string[] args)
{
Console.WriteLine("w3");
var exe = new Exe2();
exe.Do2().Wait();
Console.WriteLine("/w3");
Console.ReadKey();
}
public class Exe2
{
public async Task Do2()
{
Task task1 = new Task(() => { Console.WriteLine("t1"); Task.Delay(2000); });
Task task2 = new Task(() => { Console.WriteLine("t2"); Task.Delay(2000); });
Task task3 = new Task(() => { Console.WriteLine("t3"); Task.Delay(2000); });
await Task.WhenAll(task1, task2, task3);
}
}
The code above prints w3 not nothing else, and doesn't take the ReadKey either.
You haven't started your tasks, Task.WhenAll is essentially going to wait forever. You would have needed to call Start
Starts the Task, scheduling it for execution to the current
TaskScheduler.
task1.Start();
task2.Start();
task3.Start();
await Task.WhenAll(task1, task2, task3);
However : NEVER use the Task constructor unless you absolutely know you need it, it has several very big traps for unseasoned coders such as only supporting the action delegate and needing to be started. Instead ALWAYS use Task.Run which will start your tasks hot
Task task1 = Task.Run(() => { Console.WriteLine("t1"); Task.Delay(2000); });
Task task2 = Task.Run(() => { Console.WriteLine("t2"); Task.Delay(2000); });
Task task3 = Task.Run(() => { Console.WriteLine("t3"); Task.Delay(2000); });
Although not your problem, you should NEVER need to call Wait or Result in modern .net and even more so on an async method. Instead, use the async Main overload
static async Task Main(string[] args)
{
Console.WriteLine("w3");
var exe = new Exe2();
await exe.Do2();
Console.WriteLine("/w3");
Console.ReadKey();
}

Switch new Task(()=>{ }) for Func<Task>

In an answer to one of my other questions, I was told that use of new Task(() => { }) is not something that is a normal use case. I was advised to use Func<Task> instead. I have tried to make that work, but I can't seem to figure it out. (Rather than drag it out in the comments, I am asking a separate question here.)
My specific scenario is that I need the Task to not start right when it is declared and to be able to wait for it later.
Here is a LinqPad example using new Task(() => { }). NOTE: This works perfectly! (Except that it uses new Task.)
static async void Main(string[] args)
{
// Line that I need to swap to a Func<Task> somehow.
// note that this is "cold" not started task
Task startupDone = new Task(() => { });
var runTask = DoStuff(() =>
{
//+++ This is where we want to task to "start"
startupDone.Start();
});
//+++ Here we wait for the task to possibly start and finish. Or timeout.
// Note that this times out at 1000ms even if "blocking = 10000" below.
var didStartup = startupDone.Wait(1000);
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
await runTask;
Console.Read();
}
public static async Task DoStuff(Action action)
{
// Swap to 1000 to simulate starting up blocking
var blocking = 1; //1000;
await Task.Delay(500 + blocking);
action();
// Do the rest of the stuff...
await Task.Delay(1000);
}
I tried swapping the second line with:
Func<Task> startupDone = new Func<Task>(async () => { });
But then the lines below the comments with +++ in them don't work right.
I swapped the startupDone.Start() with startupDone.Invoke().
But startupDone.Wait needs the task. Which is only returned in the lambda. I am not sure how to get access to the task outside the lambda so I can Wait for it.
How can use a Func<Task> and start it in one part of my code and do a Wait for it in another part of my code? (Like I can with new Task(() => { })).
The code you posted cannot be refactored to make use of a Func<Task> instead of a cold task, because the method that needs to await the task (the Main method) is not the same method that controls the creation/starting of the task (the lambda parameter of the DoStuff method). This could make the use of the Task constructor legitimate in this case, depending on whether the design decision to delegate the starting of the task to a lambda is justified. In this particular example the startupDone is used as a synchronization primitive, to signal that a condition has been met and the program can continue. This could be achieved equally well by using a specialized synchronization primitive, like for example a SemaphoreSlim:
static async Task Main(string[] args)
{
var startupSemaphore = new SemaphoreSlim(0);
Task runTask = RunAsync(startupSemaphore);
bool startupFinished = await startupSemaphore.WaitAsync(1000);
Console.WriteLine(startupFinished ? "Startup Finished" : "Startup Timed Out");
await runTask;
}
public static async Task RunAsync(SemaphoreSlim startupSemaphore)
{
await Task.Delay(500);
startupSemaphore.Release(); // Signal that the startup is done
await Task.Delay(1000);
}
In my opinion using a SemaphoreSlim is more meaningful in this case, and makes the intent of the code clearer. It also allows to await asynchronously the signal with a timeout WaitAsync(Int32), which is not something that you get from a Task out of the box (it is doable though).
Using cold tasks may be tempting in some cases, but when you revisit your code after a month or two you'll find yourself confused, because of how rare and unexpected is to have to deal with tasks that may or may have not been started yet.
I always try my hardest to never have blocking behavior when dealing with anything async or any type that represents potential async behavior such as Task. You can slightly modify your DoStuff to facilitate waiting on your Action.
static async void Main(string[] args)
{
Func<CancellationToken,Task> startupTask = async(token)=>
{
Console.WriteLine("Waiting");
await Task.Delay(3000, token);
Console.WriteLine("Completed");
};
using var source = new CancellationTokenSource(2000);
var runTask = DoStuff(() => startupTask(source.Token), source.Token);
var didStartup = await runTask;
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
Console.Read();
}
public static async Task<bool> DoStuff(Func<Task> action, CancellationToken token)
{
var blocking = 10000;
try
{
await Task.Delay(500 + blocking, token);
await action();
}
catch(TaskCanceledException ex)
{
return false;
}
await Task.Delay(1000);
return true;
}
First, the type of your "do this later" object is going to become Func<Task>. Then, when the task is started (by invoking the function), you get back a Task that represents the operation:
static async void Main(string[] args)
{
Func<Task> startupDoneDelegate = async () => { };
Task startupDoneTask = null;
var runTask = await DoStuff(() =>
{
startupDoneTask = startupDoneDelegate();
});
var didStartup = startupDoneTask.Wait(1000);
Console.WriteLine(!didStartup ? "Startup Timed Out" : "Startup Finished");
}

Why is this Task not Finishing before my code passes the Wait Command

I have a task which runs a loop and delays for an interval each iteration. Once the CancellationTokenSource calls Cancel() I want my main code to Wait() for the Task.Delay(interval) to finish and the task to exit the loop before my code continues. I thought this code would work but it doesn't.
Instead my main code passes the t.Wait() before the Loop exits. Why?
Main Method Code:
var cts = new CancellationTokenSource();
CancellationToken ct = cts.Token;
var t = Task.Run(() => { MyLoopTask(200, ct); });
// Prepare information
cts.Cancel();
t.Wait();
// Send Information
Task Code
private async Task MyLoopTask(int interval, CancellationToken cancelToken)
{
while (!cancelToken.IsCancellationRequested)
{
Debug.Print(" Still In Loop ");
// Do something
await Task.Delay(interval);
}
Debug.Print(" cancelled ");
//Clean up
}
You create a task with Task.Run that fires and forgets the actual task you get back from MyLoopTask.
Task.Run is redundant here. You can just call MyLoopTask and use the task it returns.
var t = MyLoopTask(200, ct);
// ...
t.Wait();
If you still have some reason to use Task.Run you can do so by making sure the delegate waits for the actual task by awaiting it:
var t = Task.Run(async () => await MyLoopTask(200, ct));
// ...
t.Wait();

Wait for only one task to complete

Let's say I have two tasks, with the following requirements:
Both are asynchronous.
Both run in parallel
The moment one of them completes I need to know which one did.
I came up with the following code, but it just hangs after both tasks get started (the WaitAny function never returns). I am also getting a squiggly line under Run function telling me to add await inside it, but VS complains when I try to add it in front of Task.WaitAny. Should I be wrapping WaitAny in another Task? What am I doing wrong?
async void Run()
{
Task task1 = Task1();
Task task2 = Task2();
int completedTaskIdx = Task.WaitAny(task1, task2);
Debug.WriteLine("completedTaskIdx = {0}", completedTaskIdx.ToString());
}
async Task Task1()
{
Debug.WriteLine("Task 1 Start");
await Task.Delay(5000);
Debug.WriteLine("Task 1 Stop");
}
async Task Task2()
{
Debug.WriteLine("Task 2 Start");
await Task.Delay(10000);
Debug.WriteLine("Task 2 Stop");
}
Don't block the UI thread when using asnyc/await, you will cause dedlocks. Your WaitAny() causes you to get a deadlock. Use WhenAny instead, you can use Array.IndexOf( to translate the returned task back in to the index.
async Task Run()
{
Task task1 = Task1();
Task task2 = Task2();
var tasks = new[] {task1, task2};
Task completedTask = await Task.WhenAny(tasks);
//It is a good idea to await the retuned task, this is the point a execption would
//be raised if the task finished with a exception.
await completedTask;
int completedTaskIdx = Array.IndexOf(tasks, completedTask);
//.ToString() will cause you to have a bug, you are calling the
//wrong overload of WriteLine. The correct overload will call .ToString() for you.
Debug.WriteLine("completedTaskIdx = {0}", completedTaskIdx);
}
I also fixed a bug in your Debug.WriteLine( call, you where calling this overload when you wanted this overload. I also replaced your async void with async Task, you should never do asnyc void unless you are using it to match a event handler signature.

react different to multiple async calls

Imagine the following scenario :
public async Task DoMultipleWork() {
var uploadTask = UploadAsync(file);
var processingTask = Task.Run( () => DoCpuWork() );
await Task.WhenAll(uploadTask, processingTask);
Console.WriteLine("upload is done");
Console.WirteLine("processing is done");
}
How can I change that code so that it doesn't matter which one ends first, it execute some particular (sync or async) code.
So I fire the both task and when taskA or taskB ends, I just run some code (sync or async) independently of the other.
I think maybe ContinueWith but I'm not sure because it needs an another async method which is not really needed.
EDIT
As suggested by comments on answer, I want to clear that I want to execute different code depending on the task that completes, and get both Console.WriteLine executed as soon as the original task completes.
You want to use Task.WhenAny which returns the first task that completes. You can then tell which task completed by comparing to the original tasks. Before returning you should wait for the other one to complete explicitly (or wait for both with Task.WhenAll):
public async Task DoMultipleWork()
{
var uploadTask = UploadAsync(file);
var processingTask = Task.Run( () => DoCpuWork() );
var completedTask = await Task.WhenAny(uploadTask, processingTask);
Console.WriteLine("upload or processing is done");
if (completedTask == uploadTask)
{
// Upload completed
}
else
{
// Processing completed
}
await Task.WhenAll(uploadTask, processingTask) // Make sure both complete
Console.WriteLine("upload and processing are done");
}
As I describe on my blog, ContinueWith is dangerous unless you explicitly pass a scheduler. You should use await instead of ContinueWith in ~99% of cases (more detail in another blog post).
In your case:
private async Task UploadAsync(string filepath)
{
var result = await fileManager.UploadAsync(filepath);
Console.WriteLine($"Result from uploading file {result}");
}
private async Task ProcessAsync(string filepath, IProgress<T> progress)
{
await Task.Run(() => wordProcessor.Process(filepath, progress));
Console.WriteLine("processing completed");
}
...
await Task.WhenAll(UploadAsync(filepath), ProcessAsync(filepath, processingProgress));
public async Task DoMultipleWork() {
var uploadTask = UploadAsync(file);
var processingTask = Task.Run( () => DoCpuWork() );
uploadTask.ContinueWith((Task t) => Console.WriteLine("YOUR_MESSAGE"), TaskContinuationOptions.OnlyOnRanToCompletion);
processingTask.ContinueWith((Task t) => Console.WriteLine("YOUR_MESSAGE"), TaskContinuationOptions.OnlyOnRanToCompletion);
await Task.WhenAll(new []{uploadTask, processingTask});
}

Categories

Resources