C# Task.Run does not await action variable - c#

See example codes.
Console.WriteLine("start")
Task.Run(async () =>
{
await Task.Delay(3000);
});
Console.WriteLine("end");
// result
// start [3s delay] end
It works!
but below code is not working.
Action action = async () =>
{
await Task.Delay(3000);
};
Console.WriteLine("start")
Task.Run(action);
Console.WriteLine("end");
// result
// start [without delay] end
Why Task.Run does not await async action variable?
edit -------------------------
I'm so sorry. I wrote wrong code.
This is right code.
I test it on C# Interactive of VS 2017
Console.WriteLine("start");
await Task.Run(async () =>
{
await Task.Delay(3000);
});
Console.WriteLine("end");
Action action = async () =>
{
await Task.Delay(3000);
};
Console.WriteLine("start");
await Task.Run(action);
Console.WriteLine("end");

The main reason to use async and Tasks, is to make two or more methods to run simultaneously. So your method will continue running and when finished the delay, will call anything after the delay since you run another async func in a task.
If you run this method, You'll notice
finished
will be printed in the middle of the other work ( and here the other work is for loop,
Console.WriteLine("start");
Task.Run(async () =>
{
await Task.Delay(200);
Console.WriteLine("Finished");
});
for(int i = 0; i < 500; i++)
{
Console.WriteLine(i);
}
Console.WriteLine("end");
Console.ReadKey();
For some reason, It doesn't work as expected on dotnetfiddle
Even if you didn't run async in the task, they will run simultaneously.
Look at this example :
Console.WriteLine("start");
Task.Run(() =>
{
Task.Delay(2000).Wait(); // Will NOT wait !
Console.WriteLine("Finished");
});
for(int i = 0; i < 500; i++)
{
Console.WriteLine(i);
}
Console.WriteLine("end");
Console.ReadKey();
It'll continue without any delay since it's on a Task
But if you want the method to wait, First : Don't run it on a Task.
Second : call the Wait() method.
Like :
Console.WriteLine("start");
Task.Delay(2000).Wait(); // It'll wait for 2 seconds before continue.
for(int i = 0; i < 500; i++)
{
Console.WriteLine(i);
}
Console.WriteLine("end");
Console.ReadKey();

When using Task.Run without an await, this will just 'fire and forget'. Code execution will not wait for it, and it will simply run it as soon as a thread is available from the thread pool.
Typically, if you want to off load some work and wait for that work to be finished, create a separate method that returns a Task, then await that.
Example:
Console.WriteLine("start")
await DoWorkAsync();
Console.WriteLine("end");
/////////
private Task DoWorkAsync()
{
return Task.Run(async () =>
{
await Task.Delay(3000);
});
}
Note that wrapping a method body into a Task and returning it, is not suggested, and it typically leads to a bit of a code smell.
Stephen Cleary has good information about Tasks and async/await. I would read up on this:
https://blog.stephencleary.com/2012/02/async-and-await.html

Will work like this, not that obvious though:
Func<Task> action = async () =>
{
await Task.Delay(3000);
};
Console.WriteLine("start");
await Task.Run(action);
Console.WriteLine("end");

You may try this:
Action action = async () =>
{
await Task.Delay(3000);
};
Console.WriteLine("start");
Task.Run(action).Wait();
Console.WriteLine("end");

Related

await Task.Run does not actually wait

I am trying to get my head around the async-await however I wrote a small test and cannot understand the results i am getting.
Consider the following code (WPF Application, inside MainWindow):
private async void Button_Click(object sender, RoutedEventArgs e)
{
Stopwatch l_Stopwatch = new Stopwatch();
Debug.WriteLine("Entering Button_Click...");
l_Stopwatch.Start();
await DoSomethingAsync();
l_Stopwatch.Stop();
Debug.WriteLine("DoSomethingAsync completed in {0}", l_Stopwatch.ElapsedMilliseconds);
l_Stopwatch.Restart();
await DoSomethingElseAsync();
l_Stopwatch.Stop();
Debug.WriteLine("DoSomethingElseAsync completed in {0}", l_Stopwatch.ElapsedMilliseconds);
}
private async Task DoSomethingAsync()
{
Debug.WriteLine("Doing something Async");
await Task.Delay(5000);
}
private async Task DoSomethingElseAsync()
{
await Task.Run((Action)(async () => {
Debug.WriteLine("Doing something else Async");
await Task.Delay(5000);
}));
}
i would expect DoSomethingElseAsync to wait ~5000ms before continuing, however I keep getting the following
Entering Button_Click...
Doing something Async
DoSomethingAsync completed in 5005
Doing something else Async
DoSomethingElseAsync completed in 3
So at this point I think there is something fundamental that I am not understanding of the async-await way...
Can anyone shed light?
The problem is here:
await Task.Run((Action)(async () => {
Debug.WriteLine("Doing something else Async");
await Task.Delay(5000);
}));
You cast to Action delegate which doesn't return anything and cannot be awaited (there is no Task to await because Action returns void).
In your case you can simply change code to:
await Task.Run(async () => {
Debug.WriteLine("Doing something else Async");
await Task.Delay(5000);
});
OR
you can cast to Func<Task> delegate which returns Task that can be awaited:
await Task.Run((Func<Task>)(async () => {
Debug.WriteLine("Doing something else Async");
await Task.Delay(5000);
}));
Additional info:
If you see compiler generated code (SharpLab) you will get differences:
When you cast to Action compiler creates AsyncVoidMethodBuilder
When you cast to Func<Task> or do not cast at all compiler creates
AsyncTaskMethodBuilder

Wait for a task in multiple tasks at the same time

Imagine an initialization task and two workers:
var Init = Task.Run(async () =>
{
await Task.Delay(1000);
});
var TaskB = Task.Run(async () =>
{
await Init;
Console.WriteLine("B finished waiting");
await Task.Delay(10000000);
});
var TaskC = Task.Run(async () =>
{
await Init;
Console.WriteLine("C finished waiting");
await Task.Delay(10000000);
});
After 1 second, TaskB and TaskC are printing to the console and continuing as expected.
However, when the task which finishes first uses no asynchronous methods after awaiting Init, the other task await Init call does not finish:
var Init = Task.Run(async () =>
{
await Task.Delay(1000);
});
var TaskB = Task.Run(async () =>
{
await Init;
Console.WriteLine("B finished waiting");
await Task.Delay(5000);
});
var TaskC = Task.Run(async () =>
{
await Init;
Console.WriteLine("C finished waiting");
while (true) { }
});
The latter example prints only "C finished waiting" to the console. Why is that so?
Behind the scenes, async/await builds a state machine based on task continuations. You can mimic it using ContinueWith and a bit of ingenuity (the details are beyond the scope of this answer).
What you need to know is that it uses continuations, and that continuations - by default - happen syncrhonously, one at a time.
In this case, both B and C are creating continuations on Init. These continuations will run after the other. Thus, when C continues first and goes into the infinite loop, it prevents B to continue.
The solution? Well, other than avoiding infinite loops, you could use a task with asynchronous continuations. A simple way to do it on top of Task.Run is the following:
var Init = Task.Run(async () =>
{
await Task.Delay(1000);
}).ContinueWith(_ => { }, TaskContinuationOptions.RunContinuationsAsynchronously);
Addendum
As per OP comment, the infinite loop represent a blocking call. We can solve that situation by a different approach:
var TaskC = Task.Run(async () =>
{
await Init;
Console.WriteLine("C finished waiting");
await Task.Run(() => {while (true) { }});
});
This way, the blocking call is not int the continuation of Init (instead, it would be in a continuation of a continuation), and thus it won't prevent other continuations of Init to run.

The await operator is not waiting like I expected

I am working on a class DelayedExecutor that will delay the execution of an Action passed to its DelayExecute method by a certain time timeout (see code below) using the async and await statements. I also want to be able to abort the execution within the timeout interval if needed. I have written a small test to test its behavior like this:
Code for Test method:
[TestMethod]
public async Task DelayExecuteTest()
{
int timeout = 1000;
var delayExecutor = new DelayedExecutor(timeout);
Action func = new Action(() => Debug.WriteLine("Ran function!"));
var sw = Stopwatch.StartNew();
Debug.WriteLine("sw.ElapsedMilliseconds 1: " + sw.ElapsedMilliseconds);
Task delayTask = delayExecutor.DelayExecute(func);
await delayTask;
Debug.WriteLine("sw.ElapsedMilliseconds 2: " + sw.ElapsedMilliseconds);
Thread.Sleep(1000);
}
}
The output I expected from this test was that it would show me:
sw.ElapsedMilliseconds outside DelayExecute 1: ...
Ran Action!"
sw.ElapsedMilliseconds inside DelayExecute: ...
sw.ElapsedMilliseconds outside DelayExecute 2:
However I get this and do not understand why:
sw.ElapsedMilliseconds outside DelayExecute 1: 0
sw.ElapsedMilliseconds outside DelayExecute 2: 30
Ran Action!
sw.ElapsedMilliseconds inside DelayExecute: 1015
On this blog post I read:
I like to think of “await” as an “asynchronous wait”. That is to say, the async method pauses until the awaitable is complete (so it waits), but the actual thread is not blocked (so it’s asynchronous).
This seems to be inline with my expectation, so what is going on here and where is my error?
Code for DelayedExecutor:
public class DelayedExecutor
{
private int timeout;
private Task currentTask;
private CancellationToken cancellationToken;
private CancellationTokenSource tokenSource;
public DelayedExecutor(int timeout)
{
this.timeout = timeout;
tokenSource = new CancellationTokenSource();
}
public void AbortCurrentTask()
{
if (currentTask != null)
{
if (!currentTask.IsCompleted)
{
tokenSource.Cancel();
}
}
}
public Task DelayExecute(Action func)
{
AbortCurrentTask();
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
return currentTask = Task.Factory.StartNew(async () =>
{
var sw = Stopwatch.StartNew();
await Task.Delay(timeout, cancellationToken);
func();
Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
});
}
}
Update:
As suggested I modified this line inside my DelayExecute
return currentTask = Task.Factory.StartNew(async () =>
into
return currentTask = await Task.Factory.StartNew(async () =>
For this to work I needed to change the signature into this
public async Task<Task> DelayExecute(Action func)
so that my new definition is this:
public async Task<Task> DelayExecute(Action func)
{
AbortCurrentTask();
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
return currentTask = await Task.Factory.StartNew(async () =>
{
var sw = Stopwatch.StartNew();
await Task.Delay(timeout, cancellationToken);
func();
Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
});
}
However now I have the same behavior as before. Is there some way achieve what I am trying to do using my design. Or is it fundamentally flawed?
By the way, I also tried putting await await delayTask; inside my test DelayExecuteTest, but this gives me the error
Cannot await 'void'
This is the updated test-method DelayExecuteTest, which does not compile:
public async Task DelayExecuteTest()
{
int timeout = 1000;
var delayExecutor = new DelayedExecutor(timeout);
Action func = new Action(() => Debug.WriteLine("Ran Action!"));
var sw = Stopwatch.StartNew();
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 1: " + sw.ElapsedMilliseconds);
Task delayTask = delayExecutor.DelayExecute(func);
await await delayTask;
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 2: " + sw.ElapsedMilliseconds);
Thread.Sleep(1000);
}
There's a bit going on here so I'm going to post a simple answer to begin with, let's see if it suffices.
You'we wrapped a task inside a task, this needs a double await. Otherwise you're only waiting for the inner task to reach its first return point, which will (can) be at the first await.
Let's look at your DelayExecute method in more detail. Let me begin with changing the return type of the method and we'll see how this changes our perspective. The change of return type is just a clarification. You're returning a Task that is nongeneric right now, in reality you're returning a Task<Task>.
public Task<Task> DelayExecute(Action func)
{
AbortCurrentTask();
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
return currentTask = Task.Factory.StartNew(async () =>
{
var sw = Stopwatch.StartNew();
await Task.Delay(timeout, cancellationToken);
func();
Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
});
}
(note that this will not compile since currentTask is also of type Task, but this is largely irrelevant if you read the rest of the question)
OK, so what happens here?
Let's explain a simpler example first, I tested this in LINQPad:
async Task Main()
{
Console.WriteLine("before await startnew");
await Task.Factory.StartNew(async () =>
{
Console.WriteLine("before await delay");
await Task.Delay(500);
Console.WriteLine("after await delay");
});
Console.WriteLine("after await startnew");
}
When executing this I get this output:
before await startnew
before await delay
after await startnew
after await delay -- this comes roughly half a second after previous line
So why this?
Well, your StartNew returns a Task<Task>. This is not a task that now will wait for the inner task to complete, it waits for the inner task to return, which it will (can) do at its first await.
So let's see the full execution path of this by numbering the interesting lines and then explaining the order in which things happen:
async Task Main()
{
Console.WriteLine("before await startnew"); 1
await Task.Factory.StartNew(async () => 2
{
Console.WriteLine("before await delay"); 3
await Task.Delay(500); 4
Console.WriteLine("after await delay"); 5
});
Console.WriteLine("after await startnew"); 6
}
1. The first Console.WriteLine executes
nothing magical here
2. We spin up a new task with Task.Factory.StartNew and await it.
Here our main method can now return, it will return a task that will continue once the inner task has completed.
The job of the inner task is not to execute all the code in that delegate. The job of the inner task is to produce yet another task.
This is important!
3. The inner task now starts executing
It will essentially execute all the code in the delegate up to and including the call to Task.Delay, which returns a Task.
4. The inner task arrives at its first await
Since this will not already have completed (it will only complete roughly 500ms later), this delegate now returns.
Basically, the await <this task we got from Task.Delay> statement will create a continuation and then return.
This is important!
6. Our outer task now continues
Since the inner task returned, the outer task can now continue. The result of calling await Task.Factory.StartNew is yet another task but this task is just left to fend for itself.
5. The inner task, continues roughly 500ms later
This is now after the outer task has already continued, potentially finished executing.
So in conclusion your code executes "as expected", just not the way you expected.
There are several ways to fix this code, I'm simply going to rewrite your DelayExecute method to the simplest way to do what you want to do:
Change this line:
return currentTask = Task.Factory.StartNew(async () =>
Into this:
return currentTask = await Task.Factory.StartNew(async () =>
This will let the inner task start your stopwatch and reach the first await before returning all the way out of DelayExecute.
The similar fix to my LINQPad example above would be to simply change this line:
await Task.Factory.StartNew(async () =>
To this:
await await Task.Factory.StartNew(async () =>
After giving it some more thought I figured out, how to achieve what I was aiming for, which is to delay the start of an action and be able to abort it in case I need to. Credit goes to Lasse V. Karlsen for helping me understand the issue.
The answer by Lasse is correct for my original problem, which is why I accepted it. But in case somebody needs to achieve something similar to what I needed, here is how I solved it. I had to use a continuation task after the task started with StartNew. I also renamed the method AbortCurrentTask into AbortExecution(). The new version of my DelayedExecutor class is this:
public class DelayedExecutor
{
private int timeout;
private Task currentTask;
private CancellationToken cancellationToken;
private CancellationTokenSource tokenSource;
public DelayedExecutor(int timeout)
{
this.timeout = timeout;
tokenSource = new CancellationTokenSource();
}
public void AbortExecution()
{
if (currentTask != null)
{
if (!currentTask.IsCompleted)
{
tokenSource.Cancel();
}
}
}
public Task DelayExecute(Action func)
{
AbortExecution();
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
return currentTask =
Task.Delay(timeout, cancellationToken).ContinueWith(t =>
{
if(!t.IsCanceled)
{
var sw = Stopwatch.StartNew();
func();
Debug.WriteLine("sw.ElapsedMilliseconds inside DelayExecute: " + sw.ElapsedMilliseconds);
}
});
}
}
This class now does what I expected from it. These two tests I now give the expected output:
[TestMethod]
public async Task DelayExecuteTest()
{
int timeout = 1000;
var delayExecutor = new DelayedExecutor(timeout);
Action func = new Action(() => Debug.WriteLine("Ran Action!"));
var sw = Stopwatch.StartNew();
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 1: " + sw.ElapsedMilliseconds);
Task delayTask = delayExecutor.DelayExecute(func);
await delayTask;
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 2: " + sw.ElapsedMilliseconds);
}
Output:
sw.ElapsedMilliseconds outside DelayExecute 1: 0
Ran Action!
sw.ElapsedMilliseconds inside DelayExecute: 3
sw.ElapsedMilliseconds outside DelayExecute 2: 1020
and
[TestMethod]
public async Task AbortDelayedExecutionTest()
{
int timeout = 1000;
var delayExecutor = new DelayedExecutor(timeout);
Action func = new Action(() => Debug.WriteLine("Ran Action!"));
var sw = Stopwatch.StartNew();
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 1: " + sw.ElapsedMilliseconds);
Task delayTask = delayExecutor.DelayExecute(func);
Thread.Sleep(100);
delayExecutor.AbortExecution();
await delayTask;
Debug.WriteLine("sw.ElapsedMilliseconds outside DelayExecute 2: " + sw.ElapsedMilliseconds);
}
Output:
sw.ElapsedMilliseconds outside DelayExecute 1: 0
sw.ElapsedMilliseconds outside DelayExecute 2: 122

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});
}

Serializing async task and async continuation

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);

Categories

Resources