Are the await / async keywords needed under Task.Run? - c#

I have an async lambda expression wrapped under Task.Run.
However, it looks like I can drop the async and await keywords and they will produce the same result.
t1 = Task.Run(() => DoSomethingExpensiveAsync());
t2 = Task.Run(() => DoSomethingExpensiveAsync());
await Task.WhenAll(t1, t2);
vs
var t1 = Task.Run(async () => await DoSomethingExpensiveAsync());
var t2 = Task.Run(async () => await DoSomethingExpensiveAsync());
await Task.WhenAll(t1, t2);
How come the compiler let me do this and what is happening behind
the scene?
Is there a situation where adding them will make a
difference?

There are actually three variants.
var task = Task.Run(() => DoSomethingExpensiveAsync());
^ This one declares a new anonymous non-async function that calls DoSomethingExpensiveAsync() and returns its Task. The compiler compiles this anonymous function and passes it as an argument to Task.Run().
var task = Task.Run( async () => await DoSomethingExpensiveAsync() );
^ This one declares a new anonymous async function that calls DoSomethingExpensiveAsync(). It then returns an incomplete Task, waits for DoSomethingExpensiveAsync() to finish, and then signals the task as complete.
var task = Task.Run(DoSomethingExpensiveAsync);
^ This one does not declare a new anonymous function at all. A direct reference to DoSomethingExpensiveAsync will be passed as an argument to Task.Run().
All of these are valid because all three versions return a Task and therefore match the overload of Task.Run() that accepts a Func<Task>.
As a black box, all three calls will end up doing the same thing. However the first two result in a new function being compiled (although I'm not certain it wouldn't be optimized away) and the second one also results in another state machine being created for it.
The difference might be clearer if we rewrite them without using lambda expressions or anonymous functions. The following code does exactly the same thing:
//This is the same as Task.Run( () => DoSomethingExpensiveAsync());
Task Foo()
{
return DoSomethingExpensiveAsync();
}
var task = Task.Run(Foo);
//This is the same as Task.Run(async () => await DoSomethingExpensiveAsync());
async Task Bar()
{
return await DoSomethingExpensiveAsync();
}
var task = Task.Run(Bar);
The difference between these two is that one "elides" tasks while the other doesn't. Stephen Cleary has written a whole blog on the subject.

How come the compiler let me do this and what is happening behind the scene?
The overload of Task.Run that you're invoking takes a Func<Task> - that is, a Task-returning function. It doesn't matter where the Task comes from; the function just needs to return it from somewhere.
If you pass a delegate without async and await, then the delegate is just calling a Task-returning function and returns that same Task. If you pass a delegate with async and await, then the delegate calls the Task-returning function and awaits it; the actual Task returned from the delegate is created by the async keyword.
In this case, the two are semantically equivalent. Using the async/await keywords are a bit less efficient, since the compiler creates a state machine for the async delegate.
Is there a situation where adding them will make a difference?
Yes. In the general case, you should keep async and await. Only remove them in extremely simple "passthrough" situations like the one here.

Your code is the same as
t1 = DoSomethingExpensiveAsync();
t2 = DoSomethingExpensiveAsync();
await Task.WhenAll( t1, t2 );
because Task.Run( Func< function ) will return a proxy of the task generated by function. There is no other Task created, and so you are awaiting the original tasks.
When you already have an async method then there is no need to use Task.Run at all.

Without knowing what DoSomethingExpensiveAsync it's impossible to tell for certain what will happen.
Let's assume DoSomethingExpensiveAsync is something like this:
async Task DoSomethingExpensiveAsync()
{
SynchronousMethod();
await AsynchronousMethod();
}
In the first snippet, Task.Run will schedule the invocation of DoSomethingExpensiveAsync to the thread pool and returns as soon as SynchronousMethod returns.
In the second snippet, Task.Run will schedule the invocation of DoSomethingExpensiveAsync to the thread pool and returns when the Task return by DoSomethingExpensiveAsync is completed.

Related

c# async await strange warning CS1998: This async method lacks 'await' operators

i'm new to async programming, i get the below warning in this method, does somebody knows why this happens? Thank you very much.
public async Task<List<T1>> StartSearchAsync()
{
....other code
searchRequests.ForEach(async s => {
products.AddRange(await SinglePageSearch(s));
});
return products;
}
warning CS1998: This async method lacks 'await' operators and will run
synchronously. Consider using the 'await' operator to await
non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work
on a background thread.
Your await is within a lambda expression, which is another function entirely to your StartSearchAsync method.
In fact you should not be passing an async delegate to List<T>.ForEach, as that converts the delegate to async void, which is undesirable because the calling method cannot wait for the delegates to complete.
A better option would be to use Enumerable.Select, in combination with Task.WhenAll:
public async Task<List<T1>> StartSearchAsync()
{
....other code
var tasks = searchRequests.Select(SinglePageSearch);
var results = await Task.WhenAll(tasks);
foreach (result in results) products.AddRange(result);
return products;
}
Using this approach, Task.WhenAll enumerates the Tasks generated by Select, and creates another Task that completes when each SinglePageSearch has completed.
Now StartSearchAsync can await their completion.
And if products is simply an empty list being used to amalgamate the results, you can simplify further:
public async Task<List<T1>> StartSearchAsync()
{
....other code
var results = await Task.WhenAll(searchRequests.Select(SinglePageSearch));
return results.SelectMany(x => x).ToList();
}
The compiler tells you that you can safely remove the async keyword from the method signature since you are not using the await keyword directly inside the StartSearchAsync() method.
You are indeed using await in the delegate that you pass for ForEach but this doesn't affect the compilation of the StartSearchAsync() method.
The async keyword is only used to enable you to await methods in the method or delegate that you mark as async.

Difference between passing a regular and async anonymous function to Task.Run for asynchronous work

What is the difference between
ResultType result = await Task.Run(() => GetResultAsync())
and
ResultType result = await Task.Run(async() => await GetResultAsync())
I would speculate that the former would fire and forget GetResultAsync(), since it is not awaited, but then how does it get the result? I am surprised that the return type of the formerTask.Run is Task<ResultType> and not Task<Task<ResultType>>.
Both do the same from perspective of result. In both cases the overload Task.Run<TResult>(Func<Task<TResult>> function) is used which internally unwraps the task - that's why result is Task<ResultType>. The difference between them is equiavalent to the difference between
static Task<T> Compute()
{
return GetAsyncResult();
}
and
static async Task<T> Compute()
{
return await GetAsyncResult();
}
In the first case the promise is just passed back to the caller, while in the second the state machine is built by compiler around the Compute method.
In the first line the 'Task.Run' you start, immediately returns the result of 'GetResultAsync'. That result however, is a Task that can be awaited (by your 'await').
So, you actually 'await' the 'GetResultAsync' method.
In the second line, you start a Task that does not return immediately, but waits till 'GetResultAsync' (called asynchronously) is finished. The returntype of your Task is 'ResultType'.
So you 'await' your own Task, that in its turn will only return after awaiting the 'GetResultAsync' method.
In the end, they both accomplish the same result, but in a slightly different manner.

Is async and unwrap necessary for StartNew()?

I have this code:
var task = Task.Factory.StartNew(() => service.StartAsync(ct), ct);
but I'm wondering if it should instead be this:
var task = Task.Factory.StartNew(async () => await service.StartAsync(ct), ct).Unwrap();
Is the first one correct to start my async service? Or is the second one better?
Consider the type of task returned, the first one yields Task<Task<int>> while the second yields Task<int>. So really the first one is a Task representing starting of the inner task, while the second, unwrapped, represents the Task returned by the inner method representing the service starting. Finally you can also Unwrap the first and get the same effect without the async/await which is unnecessary here. None of this really covers what the need for StartNew is at all in this case just reviews the return types your looking at.
Consider the following code:
public class AsyncTesting
{
public void StartServiceTest()
{
Task<Task<int>> tsk1 = Task.Factory.StartNew(() => StartAsync());
Task<int> tsk2 = Task.Factory.StartNew(() => StartAsync()).Unwrap();
Task<int> tsk3 = Task.Factory.StartNew(async () => await StartAsync()).Unwrap();
}
public Task<int> StartAsync() => Task.Delay(2500).ContinueWith(tsk => 1);
}
The method that does not Unwrap returns a Task that represents starting the internal Task not the work it does.
As JSteward explain in their answer, the first line of code is wrong. It doesn't do what you expect it to do:
Task<Task> task = Task.Factory.StartNew(() => service.StartAsync(ct), ct); // Buggy
The second line has the correct behavior, but not because of the async/await. The async/await can be safely elided. What makes it correct is the Unwrap. It is still problematic though, because it violates the guideline CA2008 about not creating tasks without passing a TaskScheduler.
The best way to solve your problem is to use the Task.Run method:
Task task = Task.Run(() => service.StartAsync(ct), ct); // Correct
You can read about the differences between Task.Run and Task.Factory.StartNew in this article by Stephen Toub.

async await on anonamous methods

This works
Task <string> t = Task <string> .Factory.StartNew(() => {
return "test";
});
but if I change this to the below gives complilation error why?
Task <string> t = Task <string> .Factory.StartNew(asyc() => {
await Thread.sleep(10000);
return "test";
});
The reason for your issue is that the async method does not return a string but a Task<string>. So the correct return value for your function would be a Task<Task<string>>. Be aware that the outer task will be marked as completed as soon as the inner task is created and from that point forward you would have to wait for the inner task.
I suggest you are not using Task.Factory.StartNew but rather Task.Run since this function has a overload that properly handles async inner functions and unwraps the tasks. Using Task.Run is a good general idea, since you rarely need the Task created the way that Task.Factory.StartNew does.
So I suggest you change the code to:
Task<string> t = Task.Run(async () =>
{
await Task.Delay(10000);
return "test";
});
But if you are sure that you want to use Task.Factory.StartNew you can use this:
Task<Task<string>> t = Task.Factory.StartNew(async () =>
{
await Task.Delay(10000);
return "test";
});
EDIT: Just spotted that you are wrongly using Thread.Sleep. You need something that returns a Task to await it (or rather a awaitable object).
The function that does that for a delay is Task.Delay. This gives you a Task that can be awaited that completes after a set time.

async within a LINQ code - Clarification?

Almost every SO's answer regarding this topic , states that :
LINQ doesn't work perfectly with async
Also :
I recommend that you not think of this as "using async within LINQ"
But in Stephen's book there is a sample for :
Problem: You have a collection of tasks to await, and you want to do some
processing on each task after it completes. However, you want to do
the processing for each one as soon as it completes, not waiting for
any of the other tasks.
One of the recommended solutions was :
static async Task<int> DelayAndReturnAsync(int val)
{
await Task.Delay(TimeSpan.FromSeconds(val));
return val;
}
// This method now prints "1", "2", and "3".
static async Task ProcessTasksAsync()
{
// Create a sequence of tasks.
Task<int> taskA = DelayAndReturnAsync(2);
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
var tasks = new[] { taskA, taskB, taskC };
var processingTasks = tasks.Select(async t =>
{
var result = await t;
Trace.WriteLine(result);
}).ToArray();
// Await all processing to complete
await Task.WhenAll(processingTasks);
}
Question #1:
I don't understand why now async inside a LINQ statement - does work . Didn't we just say "don't think about using async within LINQ" ?
Question #2:
When the control reaches the await t here — What is actually happen? Does the control leaves the ProcessTasksAsync method ? or does it leaves the anonymous method and continue the iteration ?
I don't understand why now async inside a LINQ statement - does work . Didn't we just say "don't think about using async within LINQ" ?
async mostly doesn't work with LINQ because IEnumerable<T> extensions don't always infer the delegate type properly and defer to Action<T>. They have no special understanding of the Task class. This means the actual async delegate becomes async void, which is bad. In the case of Enumerable.Select, we have an overload which returns a Func<T> (which in turn will be Func<Task> in our case), which is equivalent to async Task, hence it works fine for async use-cases.
When the control reaches the await t here — What is actually happen? Does the control leaves the ProcessTasksAsync method ?
No, it doesn't. Enumerable.Select is about projecting all elements in the sequence. This means that for each element in the collection, await t which will yield control back to the iterator, which will continue iterating all elements. That's why you later have to await Task.WhenAll, to ensure all elements have finished execution.
Question 1:
The difference is that each task is continued with additional processing which is: Trace.WriteLine(result);. In the link you pointed to, that code does not change anything, just creates overhead of awaiting and wrapping with another task.
Question 2:
When the control reaches the await t here — What is actually happen?
It awaits for the result of ProcessTasksAsync's task, then continue with Trace.WriteLine(result);. We can say that the control leaves the ProcessTasksAsync method when we have the result and the processing is still inside the anonymous method.
At the end, we have await Task.WhenAll(processingTasks); which will await for all tasks including the additional processing (Trace.WriteLine(result);) to complete before continuing but each task does not await for the others to continue executing: Trace.WriteLine(result);
It will be better this way:
static async Task<int> DelayAndReturnAsync(int val)
{
await Task.Delay(TimeSpan.FromSeconds(val));
return val;
}
static async Task AwaitAndProcessAsync(Task<int> task)
{
var result = await task;
Console.WriteLine(result);
}
// This method now prints "1", "2", and "3".
static async Task ProcessTasksAsync()
{
// Create a sequence of tasks.
Task<int> taskA = DelayAndReturnAsync(2);
Task<int> taskB = DelayAndReturnAsync(3);
Task<int> taskC = DelayAndReturnAsync(1);
var tasks = new[] { taskA, taskB, taskC };
var processingTasks = tasks.Select(AwaitAndProcessAsync).ToArray();
// Await all processing to complete
await Task.WhenAll(processingTasks);
}
Array of Task, because AwaitAndProcessAsync returns Task.

Categories

Resources