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.
Related
I have a series of methods (with variable number of parameters) that return a Task
I want to create a method that does something before and after each of this methods, by passing the Task.
I've simplified everything (removed cancellationToken, actual processing, etc) in this sample:
public async Task<string> GetDataString()
{
Console.WriteLine("Executing");
return "test";
}
public async Task<T> Process<T>(Task<T> task)
{
Console.WriteLine("Before");
var res = await task;
Console.WriteLine("After");
return res;
}
And in my main:
Task<string> task = GetDataString();
string result = await Process<string>(tasks);
Console.WriteLine(res);
the console output is
Executing
Before
After
test
What can I do to create the task but not actually starting it? And starting it only before the wait?
I managed to do it by creating a PauseToken, as explained in this article:
https://devblogs.microsoft.com/pfxteam/cooperatively-pausing-async-methods/
but I wonder if is there a better way.
Thanks,
Mattia
Your generic ProcessAsync method could accept a task factory as argument:
public async Task<T> ProcessAsync<T>(Func<Task<T>> taskFactory)
{
Console.WriteLine("Before");
var res = await taskFactory();
Console.WriteLine("After");
return res;
}
This way the task will be created at the time you'll invoke the factory method. You are in control of its creation.
Here is an example of calling the ProcessAsync method, passing as factory a lambda:
var result = await ProcessAsync(() => GetDataStringAsync(arg1, arg2));
This way you are not restricted to a factory method without arguments.
For completeness I should mention that Task objects can also created in a cold state using the constructor new Task(), and started later using the Start method, but this approach is not recommended.
You can remove the async keyword (from GetDataString) and create a new task which will be executed when you await
so the result of the code below is : before , executing , test , after
private static async Task Start()
{
Task<string> task = GetDataString();
string result = await Process<string>(task);
Console.WriteLine(result);
Console.ReadLine();
}
public Task<string> GetDataString()
{
return new TaskFactory(TaskScheduler.Default).StartNew(() =>
{
Console.WriteLine("Executing");
return "test";
});
}
public async Task<T> Process<T>(Task<T> task)
{
Console.WriteLine("Before");
var res = await task;
Console.WriteLine("After");
return res;
}
I have this method:
private static async Task MyMethod();
And it is invocated this way:
public static void Main()
{
s_Finishing = false;
Task printTask = PrintStatistics();
MyMethod(serversSawa, serversSterling).Wait();
s_Finishing = true;
}
I expect that PrintStatistics will stop to run only after MyMethod is completed. But unfortunately it doesn`t. If I comment the line s_Finishing = true; The task runs forever - and allows to MyMethod to be completed
How can I solve the issue?
private static async Task PrintStatistics()
{
while (!s_Finishing)
{
long total = 0;
await Task.Delay(TimeSpan.FromSeconds(20));
foreach (var statistic in s_Statistics)
{
ToolsTracer.Trace("{0}:{1}", statistic.Key, statistic.Value);
total += statistic.Value;
}
foreach (var statistic in s_StatisticsRegion)
{
ToolsTracer.Trace("{0}:{1}", statistic.Key, statistic.Value);
}
ToolsTracer.Trace("TOTAL:{0}", total);
ToolsTracer.Trace("TIME:{0}", s_StopWatch.Elapsed);
}
}
private static async Task MyMethod()
{
Parallel.ForEach(
data,
new ParallelOptions { MaxDegreeOfParallelism = 20 }, async serverAndCluster =>
{
await someMethod() });
}
I believe your problem is here:
Parallel.ForEach(..., async ...);
You can't use async with ForEach. It's extremely rare to need to do both parallel (CPU-bound) and async (I/O-bound) together in the same method. If you just want concurrency (which I suspect), use Task.WhenAll instead of ForEach. If you really do need both CPU parallelism and async, then use TPL Dataflow.
I'm curious about how the flow of async works across the stack. When reading about async in C#, you will continually read some version of the following:
If the task we are awaiting has not yet completed then sign up the
rest of this method as the continuation of that task, and then return
to your caller immediately; the task will invoke the continuation when
it completes.
It's the return to your caller immediately part that confuses me. In the below example, assuming something calls MethodOneAsync(), execution hits the await in MethodOneAsync. Does it immediately return to the method that called this? If so, how does MethodTwoAsync ever get executed? Or does it continue down the stack until it detects that it's actually blocked (ie. DoDBWorkAsync()) and then yield the thread?
public static async Task MethodOneAsync()
{
DoSomeWork();
await MethodTwoAsync();
}
public static async Task MethodTwoAsync()
{
DoSomeWork();
await MethodThreeAsync();
}
public static async Task MethodThreeAsync()
{
DoSomeWork();
await DoDBWorkAsync();
}
The part before an await in an async method is executed synchronously. That's the case for all async methods.
Let's assume that instead of await DoDBWorkAsync() we have await Task.Delay(1000).
That means MethodOneAsync starts running, executes DoSomeWork and calls MethodTwoAsync which in turn executes DoSomeWork which calls MethodThreeAsync which again executes DoSomeWork.
Then it calls Task.Delay(1000), gets back an uncompleted Task and awaits it.
That await is logically equivalent to adding a continuation and returning the task back to the caller, which is MethodTwoAsync which does the same and return a Task to the caller and so forth and so forth.
That way when the root delay Task completes all the continuations can run one after the other.
If we make your example a bit more complicated:
public static async Task MethodOneAsync()
{
DoSomeWorkOne();
await MethodTwoAsync();
DoMoreWorkOne();
}
public static async Task MethodTwoAsync()
{
DoSomeWorkTwo();
await MethodThreeAsync();
DoMoreWorkTwo();
}
public static async Task MethodThreeAsync()
{
DoSomeWorkThree();
await Task.Delay(1000);
DoMoreWorkThree();
}
It would be logically similar to doing this with continuations:
public static Task MethodOneAsync()
{
DoSomeWorkOne();
DoSomeWorkTwo();
DoSomeWorkThree();
return Task.Delay(1000).
ContinueWith(_ => DoMoreWorkThree()).
ContinueWith(_ => DoMoreWorkTwo()).
ContinueWith(_ => DoMoreWorkOne());
}
First, let me do a different example so my code further down will make sense
public static async Task MethodOneAsync()
{
DoSomeWork1();
await MethodTwoAsync();
DoOtherWork1();
}
public static async Task MethodTwoAsync()
{
DoSomeWork2();
await MethodThreeAsync();
DoOtherWork2();
}
public static async Task MethodThreeAsync()
{
DoSomeWork3();
await DoDBWorkAsync();
DoOtheWork3();
}
All async await does is turn the above code in to something similar to (but even this is a HUGE simplification) this
public static Task MethodOneAsync()
{
DoSomeWork1();
var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
var resultTask = MethodTwoAsync();
return resultTask.ContinueWith((task) =>
{
syncContext.Post((state) =>
{
SynchronizationContext.SetSynchronizationContext(syncContext);
DoOtherWork1();
}, null);
});
}
public static Task MethodTwoAsync()
{
DoSomeWork2();
var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
var resultTask = MethodThreeAsync();
return resultTask.ContinueWith((task) =>
{
syncContext.Post((state) =>
{
SynchronizationContext.SetSynchronizationContext(syncContext);
DoOtherWork2();
}, null);
});
}
public static Task MethodThreeAsync()
{
DoSomeWork3();
var syncContext = SynchronizationContext.Current ?? new SynchronizationContext();
var resultTask = DoDbWorkAsync();
return resultTask.ContinueWith((task) =>
{
syncContext.Post((state) =>
{
SynchronizationContext.SetSynchronizationContext(syncContext);
DoOtherWork3();
}, null);
});
}.
Each await just executes the next layer deeper till it is forced to return a Task, once that happens it starts doing continuations on the tasks when they complete and in that continuation it passes in a delegate representing "the rest of the function" to SynchronizationContext.Post( to get the code scheduled to be executed.
How it is scheduled depends on which SynchronizationContext you are in. In WPF and Winforms it queues it to the message pump, for the default and ASP.NET it queues it on a thread pool worker.
It does return after firing the Task for MethodTwoAsync method (or continues execution, if this Task is immediately done), so the inner Task is executing into SynchronizationContext.Current environment.
And after this Task is done, the .NET state machine return the execution to the point right after the MethodTwoAsync firing, and process the rest of the code.
See more information at MSDN article:
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.
i have below code, looks like the await statement in ApiClass will cause function "AControllerMethodInAspMVC" to return earlier before each api.GetResultFromAnotherService is finished.
the main thread return before all children thread finished. is there a way to fix this issue?
private ApiClass api = new ApiClass();
[HttpPost]
public Task<JsonResult> AControllerMethodInAspMVC()
{
var arrayOfItem = …;
List<object> resultObjs = new List<object>();
var resultLock = new SemaphoreSlim(1);
Parallel.ForEach(
arrayOfItem,
async item =>
{
var result = await api.GetResultFromAnotherService(item.id);
var resultObj = new {
// prepare resultObj from result
};
await resultLock.WaitAsync();
resultObjs.add(resultObj);
resultLock.Release();
});
return Task.FromResult(this.Json(resultObjs));
}
Public class ApiClass
{
Public async Task<string> GetResultFromAnotherService(string id)
{
….
…
await Call AnAsyncOperationToGetResult
…
…
}
}
Parallel.ForEach() does not understand async, so your lambda is compiled as async void. What this means is that as soon as you hit the await, the ForEach() thinks the iteration is complete and continues with another iteration.
One way to fix that would be to first start all of the service calls at the same time and then wait for all of them to complete using Task.WhenAll():
public async Task<JsonResult> AControllerMethodInAspMVC()
{
var arrayOfItem = …;
var tasks = arrayOfItem.Select(
item => api.GetResultFromAnotherService(item.id));
return await Task.WhenAll(tasks);
}
If you want to limit how many times is the service call executed in parallel, you could use SemaphoreSlim's WaitAsync():
var semaphore = new SemaphoreSlim(degreeOfParallelism);
var tasks = arrayOfItem.Select(
async item =>
{
await semaphore.WaitAsync();
try
{
return await api.GetResultFromAnotherService(item.id);
}
finally
{
sempahore.Release();
}
});