This question already has answers here:
Why use async and return await, when you can return Task<T> directly?
(9 answers)
Closed 4 years ago.
First I used async delegates in this way:
public async Task TestWithAsyncDelegate()
{
await MethodWithAsyncDelegate(async (counter) => await AsyncLoad(counter));
}
But after some time, I found that if the used delegate contains only one call, it can be simplified to:
public async Task TestWithoutAsyncDelegate()
{
await MethodWithAsyncDelegate(counter => AsyncLoad(counter));
}
Is this code equivalent? In this case, embedding lambda in another delegate, it is redundant in my opinion. So I am using it.
Does somebody have an explanation why?
This approach cannot be used if called lambda has to await in its body:
public async Task TestWithMultipleCalls()
{
await MethodWithAsyncDelegate(async (counter) =>
{
await Task.Delay(10);
await AsyncLoad(counter);
});
}
Complete code follows:
public class AsyncDelegateTests
{
[Fact]
public async Task TestWithAsyncDelegate()
{
await MethodWithAsyncDelegate(async (counter) => await AsyncLoad(counter));
}
[Fact]
public async Task TestWithoutAsyncDelegate()
{
await MethodWithAsyncDelegate(counter => AsyncLoad(counter));
}
[Fact]
public async Task TestWithMultipleCalls()
{
await MethodWithAsyncDelegate(async (counter) =>
{
await Task.Delay(10);
await AsyncLoad(counter);
});
}
/// <summary>
/// simulate async work
/// </summary>
private async Task AsyncLoad(int counter)
{
await Task.Delay(10);
}
private async Task MethodWithAsyncDelegate(Func<int, Task> asyncDelegate)
{
for (int i = 0; i < 10 ; i++)
{
await asyncDelegate(i);
}
}
}
In the first example c# will wrap your async,await in another task because of the async method or lambda which has a very slight impact in performance
in the other example though it returns the task as is and it's up to the one that needs to await or consume the value to await it.
if you are not going to use the value in that lambda it's best to just return the task to prevent wrapping your task in another task
Related
I am writing a wrapper function that executes an arbitrary number of async tasks and will provide retry and error handling policy. I'm having an issue awaiting the result of the async tasks.
The method call looks like this:
Execute(async () => await someAsyncFunction(someValue), async () await someOtherFunction(someValue))
My method implementation looks like this:
void Execute<T1, T2>(Func<T1> fn1, Func<T2> fn2, ... /* overloads for up to 6 functions */)
{
fn1();
fn2();
/* ... */
}
I've not yet applied the error handling and retry policy, but from debugging I've noticed that stepping over fn1 or fn2 is immediate, even when I put a large delay in, for example:
async Task someAsyncFunction(object value)
{
await Task.Delay(10000);
//...
}
Is it possible to achieve what I want with async methods?
An async "action" is actually a function that returns a Task, so it'll be a Func<Task>. You can create a collection of tasks and then await them all with Task.WhenAll. You can supply a flexible number of arguments using the params keyword.
Note also that Execute() must itself be async in order to make async calls.
public class Program
{
static async Task Execute(params Func<Task>[] actions)
{
var tasks = actions.Select( action => action() );
await Task.WhenAll(tasks);
}
public static async Task MainAsync()
{
await Execute
(
async () =>
{
await Task.Delay(3000);
Console.WriteLine("Function 1 done");
}
,
async () =>
{
await Task.Delay(3000);
Console.WriteLine("Function 2 done");
}
);
}
public static void Main()
{
MainAsync().GetAwaiter().GetResult();
}
}
Output:
Function 2 done
Function 1 done
Example code on DotNetFiddle
I found this class in another answer Best way in .NET to manage queue of tasks on a separate (single) thread.
I wanted to try it out, but the syntax is a bit strange to me. Trying to just kick off a dummy task that returns a single int. I can't get this to compile, not sure what the syntax problem is.
m_TaskQueue.Enqueue<int>(
() => { return 1; }
);
Compiler error:
Cannot convert lambda expression to delegate type System.Func<System.Threading.Tasks.Task<int>> because some of the return types in the block are not implicitly convertible to the delegate return type
Class from other answer:
public class TaskQueue
{
private SemaphoreSlim semaphore;
public TaskQueue()
{
semaphore = new SemaphoreSlim(1);
}
public async Task<T> Enqueue<T>(Func<Task<T>> taskGenerator)
{
await semaphore.WaitAsync();
try
{
return await taskGenerator();
}
finally
{
semaphore.Release();
}
}
public async Task Enqueue(Func<Task> taskGenerator)
{
await semaphore.WaitAsync();
try
{
await taskGenerator();
}
finally
{
semaphore.Release();
}
}
}
To add to the other answers which suggest using Task.FromResult to create a task, you could also use an async lambda expression - this would be useful if you want to use await within the body of the lambda expression:
m_TaskQueue.Enqueue<int>(
async () => {
await Task.Delay(1000); // For example
return 1;
}
);
As the error is trying to tell you, you need a function that returns a Task<T>.
You can use Task.FromResult(1) to create one.
I have the following code:
public Index () {
InitializeIndexAsync();
}
async Task InitializeIndexAsync () {
State = IndexState.Initializing;
await Task.Factory.StartNew(async () => {
// Initialize other things.
await IndexAsync();
});
State = IndexState.Ready;
}
I would expect that "State = IndexState.Ready" would not be hit until the asynchronous lambda completes, but debugging shows that line is hit long before the thread started above it completes. Why is this?
StartNew does not understand async lambdas, so when you pass it an async lambda, it will return a Task<Task>. Conceptually, the "outer" task only represents the start of the async lambda; the "inner" task represents the completion of the async lambda.
This is one of the reasons that StartNew is the wrong choice for async code, as I explain on my blog. A better solution is to use Task.Run, which was designed with async in mind:
async Task InitializeIndexAsync () {
State = IndexState.Initializing;
await Task.Run(async () => {
// Initialize other things.
await IndexAsync();
});
State = IndexState.Ready;
}
Not sure what you are trying to achieve by all these awaits...
I would try to keep it simple by having a synchronously method which initializes things, and then another MethodAsync which returns a Task and I can await on that task:
public async void Index()
{
await InitializeIndexAsync();
}
private Task InitializeIndexAsync()
{
return Task.Factory.StartNew(() => InitializeIndex());
}
private void InitializeIndex()
{
State = IndexState.Initializing;
// Initialize other things synchronously.
IndexAsync().Wait();
State = IndexState.Ready;
}
I hope this is what you meant.
According to MSDN:
You can use the AttachedToParent option to express structured task
parallelism, because the parent task implicitly waits for all child
tasks to finish.
So I have this code:
public async Task<int> GetIntAsync()
{
var childTask = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
},TaskCreationOptions.AttachedToParent);
return 1;
}
public async Task<ActionResult> Index()
{
var watch = Stopwatch.StartNew();
var task = GetIntAsync();
var result = await task;
var time = watch.ElapsedMilliseconds;
return View();
}
I would like to know why the time is 0 and not 1000.
Code that uses the Task-based Asynchronous Pattern (TAP) does not normally use AttachedToParent. AttachedToParent was part of the design of the Task Parallel Library (TPL). Both the TPL and TAP share the same Task type, but there are many TPL members that should be avoided in TAP code.
In TAP, you can support the notion of "parent" and "child" async methods by having the "parent" async method await the task returned from the "child" async method:
public async Task<int> GetIntAsync()
{
var childTask = Task.Run(() =>
{
...
await Task.Delay(1000);
...
});
...
await childTask;
return 1;
}
AttachedToParent only attaches to tasks that are scheduled. The Task returned by your async method is not scheduled, but rather comes (implicitly) from a TaskCompletionSource
This is a solution that would work for a dynamic number of child tasks.
Using a list would be, in general, naive.
public async Task<int> GetIntAsync()
{
var childTasks = new List<Task>();
while (...)
{
...
childTasks.Add(Task.Run(...));
...
}
await Task.WhenAll(childTasks);
return 1;
}
For example, if the child tasks are short lived and, new ones are created rapidly and unboundedly, then the list would overflow.
Instead, we can use just one task.
public async Task<int> GetIntAsync()
{
var childrenTask = Task.WhenAll();
while (...)
{
...
childrenTask = Task.WhenAll(Task.Run(...), childrenTask);
...
}
await childrenTask;
return 1;
}
Note this is a linked list of tasks which shrinks as soon as a task completes.
I'm trying to get the hand of the new async CTP stuff and I'm probably confusing myself here..
I can have this "task method", with no problem:
public static Task<String> LongTaskAAsync() {
return Task.Run(() => {
return("AAA");
});
}
But what if I need the task to execute another task, can I mark it as "async" and use "await"? I tried this:
public async static Task<String> LongTaskAAsync() {
await Task.Delay(2000);
return Task.Run(() => {
return("AAA");
});
}
But then mysteriously get this compiler error: Since this is an async method, the return expression must be of type 'string' rather than Task<string>
What am I missing here?
You may want to read my async/await intro post.
Return values from async methods are wrapped in a Task<TResult>. Likewise, await unwraps those return values:
public static async Task<String> LongTaskAAsync() {
await Task.Delay(2000);
return await Task.Run(() => {
return("AAA");
});
}
The reasoning behind this is described in my Async "Why Do the Keywords Work That Way" Unofficial FAQ.
P.S. You can also use Task.FromResult for simple tests like this.
Edit: If you want to create and return the Task object itself, then the method should not be async. One somewhat common pattern is to have a public non-async method that calls the async portion only if necessary.
For example, some kind of asynchronous cache - if the object is in the cache, then return it immediately; otherwise, asynchronously create it, add it to the cache, and return it (this is example code - not thread-safe):
public static Task<MyClass> GetAsync(int key)
{
if (cache.Contains(key))
return Task.FromResult(cache[key]);
return CreateAndAddAsync(key);
}
private static async Task<MyClass> CreateAndAddAsync(int key)
{
var result = await CreateAsync(key);
cache.Add(key, result);
return result;
}
Can a “task method” also be an “async” method?
Yes it can be, by simply changing the method signature to public async static Task<Task<String>> LongTaskAAsync() since that is, what it will return.
If you use the async keyword, the runtime will wrap the type you return into a task, to enable asynchronousness. Say if you return a string, the runtime will wrap that into a Task<string>. int will go Task<int> and Task<string> will go Task<Task<string>>. See this console app to clearify:
public class Program
{
public static void Main(string[] args)
{
// start the main procedure asynchron
Task.Run(() => DoIt()).Wait();
}
// for async support since the static main method can't be async
public static async void DoIt()
{
Program p = new Program();
// use the methods
string s = await p.GetString();
int i = await p.GetInt();
Task<string> tsk = await p.GetTaskOfString();
// just to prove the task works:
// C# 5
string resultFromReturnedTask = await tsk;
// C# 4
string resultFromReturnedTask2 = tsk.Result;
}
public async Task<string> GetString()
{
return "string";
}
public async Task<int> GetInt()
{
return 6;
}
public async Task<Task<string>> GetTaskOfString()
{
return Task.Run(() => "string");
}
}