I have Task, Task1 and Task2. Task1 and Task2 are independent from each other, but both depend on the result of Task. I can make it work this way:
static async Task Test1()
{
var task = Task.Delay(1000);
var task1 = task.ContinueWith(_ =>
{
Console.WriteLine("Task1, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
var task2 = task.ContinueWith(_ =>
{
Console.WriteLine("Task2, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
await Task.WhenAll(task1, task2);
}
Or, alternatively, this way:
static async Task Test2()
{
var task = Task.Delay(1000);
await task;
var task1 = Task.Run(() =>
{
Console.WriteLine("Task1, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
var task2 = Task.Run(() =>
{
Console.WriteLine("Task2, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
await Task.WhenAll(task1, task2);
}
Which way should I prefer? Are they both equally efficient? Is there a better way of composing this?
I think a better way of composing this would be to create separate async methods for the follow-up Tasks. Something like:
private static async Task Task1(Task task)
{
await task;
Console.WriteLine("Task1, thread: {0}", Thread.CurrentThread.ManagedThreadId);
}
private static async Task Task2(Task task)
{
await task;
Console.WriteLine("Task2, thread: {0}", Thread.CurrentThread.ManagedThreadId);
}
private static async Task Test1()
{
var task = Task.Delay(1000);
var task1 = Task1(task);
var task2 = Task2(task);
await Task.WhenAll(task1, task2);
}
This will act differently than your code with regard to synchronization context (if one is present), but you can affect that by using await task.ConfigureAwait(false).
This is really not an answer, just another alternative
var task = Task.Delay(1000);
await task.ContinueWith(_ =>
{
Parallel.Invoke(
() => {
Console.WriteLine("Task1, thread: {0}",Thread.CurrentThread.ManagedThreadId);
},
() => {
Console.WriteLine("Task2, thread: {0}", Thread.CurrentThread.ManagedThreadId);
});
});
Related
Are those lines behaving exactly the same (Including Exceptionhandling via AggregateException)?
Task.WhenAll(taskList).Wait()
Task.WaitAll(taskList)
Thanks
Let's find out by experimentation:
var task1 = Task.FromResult(13);
var task2 = Task.FromCanceled<int>(new CancellationToken(true));
var task3 = Task.FromCanceled<int>(new CancellationToken(true));
var task4 = Task.FromException<int>(new ApplicationException());
var task5 = Task.FromException<int>(new OverflowException());
Test("Successful+Canceled+Canceled", new[] { task1, task2, task3 });
Test("Successful+Failed+Failed", new[] { task1, task4, task5 });
Test("Successful+Canceled+Failed+Failed", new[] { task1, task2, task4, task5 });
Test("Successful+Canceled+Canceled+Failed", new[] { task1, task2, task3, task4 });
static void Test(string title, Task<int>[] tasks)
{
Console.WriteLine();
Console.WriteLine(title);
try { Task.WaitAll(tasks); }
catch (AggregateException ex)
{
Console.WriteLine($"WaitAll(): {ToString(ex)}");
}
try { Task.WhenAll(tasks).Wait(); }
catch (AggregateException ex)
{
Console.WriteLine($"WhenAll.Wait(): {ToString(ex)}");
}
}
static string ToString(AggregateException aex) {
return $"({aex.InnerExceptions.Count}) " +
String.Join(", ", aex.InnerExceptions.Select(ex => ex.GetType().Name));
}
Output:
Successful+Canceled+Canceled
WaitAll(): (2) TaskCanceledException, TaskCanceledException
WhenAll.Wait(): (1) TaskCanceledException
Successful+Failed+Failed
WaitAll(): (2) ApplicationException, OverflowException
WhenAll.Wait(): (2) ApplicationException, OverflowException
Successful+Canceled+Failed+Failed
WaitAll(): (3) TaskCanceledException, ApplicationException, OverflowException
WhenAll.Wait(): (2) ApplicationException, OverflowException
Successful+Canceled+Canceled+Failed
WaitAll(): (3) TaskCanceledException, TaskCanceledException, ApplicationException
WhenAll.Wait(): (1) ApplicationException
Try it on Fiddle.
What we see is that the Task.WaitAll() method propagates the exceptions of the tasks as they are, while the Task.WhenAll().Wait() approach propagates only a single TaskCanceledException, and only when no other type of exception has occurred.
It should also be mentioned that with the Task.WaitAll you get more options out of the box, like millisecondsTimeout, or cancellationToken, or both.
I need to run multiple task to access to database and to wait to all task finished to obtain the result and assign it to my viewmodel, i try many sample but i have never have the result, any help please, this my code
var model = new AggregationViewModel();
var loadDataTasks = new Task[]
{
Task.Run(async () =>model.Alive = await _repository.GetCountAsync<filter>(Predicate)),
Task.Run(async () => model.Delay = await _repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused")))
};
try
{
await Task.WhenAll(loadDataTasks);
foreach (var item in loadDataTasks)
{
}
}
catch (Exception ex)
{
}
Check this out:
var task1 = _repository.GetCountAsync<filter>(Predicate);
var task2 = _repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused"));
await Task.WhenAll(task1, task2); //wait both tasks to finish
model.Alive = await task1;
model.Delay = await task2;
PS: the function, which the above code is placed, is needed to be async for the above scenario.
Try this code:
var model = new AggregationViewModel();
//----------------------------------------
var tasks = new List<Task<int>>();
//Add CountA
tasks.Add(_repository.GetCountAsync<filter>(Predicate));
//Add CountB
tasks.Add(_repository.GetCountAsync<Flight>(predicate.And(x => x.Status == "refused")));
//----------------------------------------
// Create a task with "allTasks" name to wait to complete all tasks
Task allTasks = Task.WhenAll(tasks);
try {
// Wait to compelete "allTask"
allTasks.Wait();
}
catch(AggregateException)
{}
//----------------------------------------
//Working on the results
if (allTasks.Status == TaskStatus.RanToCompletion) {
model.CountA=allTasks.Result[0]
model.CountB=allTasks.Result[1]
}
// Display information on faulted tasks.
else {
foreach (var t in tasks) {
Console.WriteLine("Task {0}: {1}", t.Id, t.Status);
}
}
The following code is used to simulate the time-consuming works.
async Task DoWork(string n)
{
for (var i = 1; i <= 5; i++)
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine($"{n} runs {i} seconds. {DateTime.Now}");
}
}
async Task<int> F1()
{
await DoWork("f1");
return 1;
}
async Task<string> F2()
{
await DoWork("f2");
return "X";
}
The following code will run F1() and F2() sequentially.
async Task<string> Main()
{
var f1Task = F1();
var f2Task = F2();
var f1 = await f1Task;
var f2 = await f2Task;
return $"{f1} {f2}";
}
await Main();
The following code can make them run in parallel. However, it looks cumbersome.
async Task<string> Main2()
{
int f1 = 0;
async Task G1() { f1 = await F1(); }
string f2 = "";
async Task G2() { f2 = await F2(); }
await Task.WhenAll(Task.Run(() => G1()), Task.Run(() => G2()));
return $"{f1} {f2}";
}
await Main2();
Is there a way to do it without wrapping them in Task.Run()?
You can use WhenAll() method to have your task run parallel like
async Task<int> Main()
{
var f1Task = F1();
var f2Task = F2();
await Task.WhenAll(f1Task,f2Task);
return await f1Task + await f2Task;
}
await itself doesn't start the asynchronous operation.
Kick off DoWork with a Task like this:
async Task DoWork(string n)
{
await Task.Run(() => {
for (var i = 1; i <= 5; i++)
{
System.Threading.Thread.Sleep(1000);
Console.WriteLine($"{n} runs {i} seconds. {DateTime.Now}");
}
});
}
Then you cando this:
{
var f1Task = F1();
var f2Task = F2();
return await f1Task + await f2Task;
}
You're right that it looks cumbersome. However, it can be done in a neat way. You can create a tasks array and then use WhenAll to wait for all of them at once.
var tasks = new List<Task<int>> { F1(), F2() };
var result = Task.WhenAll(tasks).GetAwaiter().GetResult();
The only condition is that all tasks has to return the same result type. Then result in this case is array of int.
I wrote a nice article about parallel processing, you can have a look at how this can be accomplished when a task is a REST call: http://www.michalbialecki.com/2018/04/19/how-to-send-many-requests-in-parallel-in-asp-net-core/
It will be both io and CPU heavy.
If you have just I/O (i.e., await Task.Delay instead of Thread.Sleep), then you can use asynchronous concurrency:
var task1 = F1();
var task2 = F2();
await Task.WhenAll(task1, task2);
int f1 = await task1;
string f2 = await task2;
However, since you have CPU as well (and since your method is synchronous), you'll need to push that to another thread and make it parallel.
Is there a way to do it without wrapping them in Task.Run()?
Yes, but Task.Run is the simplest solution here. It can look less cumbersome than your example:
var task1 = Task.Run(() => F1());
var task2 = Task.Run(() => F2());
await Task.WhenAll(task1, task2);
int f1 = await task1;
string f2 = await task2;
Technically, the Task.WhenAll is optional in both of these examples, but I like it since it makes the semantics more clear.
My code is an async api call, and looks like the example below
public async Task<IEnumerable<TaskObject>> GetTaskObjects()
{
var tasks = new List<Task<TaskObject>>();
var shizzle = Task.Run(() => { Thread.Sleep(2000); return new TaskObject("1"); });
var shizzle2 = Task.Run(() => { Thread.Sleep(1000); return new TaskObject("2"); });
tasks.Add(shizzle.ContinueWith(part1 => { Thread.Sleep(1000); return part1.Result; }));
tasks.Add(shizzle2.ContinueWith(part1 => { Thread.Sleep(1000); return part1.Result; }));
await Task.WhenAll(tasks);
return tasks.Select(x => x.Result).ToList();
}
The controller is a Stateless Service Fiber Web Api that makes some calls to a statefull service. Is this a good solution? Are there beter ones? Are the async and await keywords even necessary if this is an api call?
public async Task<IEnumerable<TaskObject>> GetTaskObjects2()
{
var tasks = new List<Task<TaskObject>>();
var shizzle = Task.Run(() => { Thread.Sleep(2000); return new TaskObject("1"); });
var shizzle2 = Task.Run(() => { Thread.Sleep(1000); return new TaskObject("2"); });
//Add your task to the collection
tasks.Add(shizzle);
tasks.Add(shizzle2);
//wait for when all task are finished and it will return the data.
return await Task.WhenAll(tasks);
}
If this line of codes are really awaitable
/// At this point, all two tasks are running at the same time.
var shizzle = DoShizzleAsync();
var shizzle2 = DoShizzle2Async();
await Task.WhenAll(shizzle2, shizzle);
See Stephen Cleary blog for more information
Given the following code, is it possible to define scheduler, creation, and continuation settings for instances of Task doThing?
I want to be able to schedule the multiple instances of doThing so they actually run exclusively from other instances (even when they are awaiting other subtasks).
private static async Task doThing(object i)
{
Console.WriteLine("in do thing {0}", (int)i);
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("out of do thing {0}", (int)i);
}
static void Main(string[] args)
{
CancellationTokenSource source = new CancellationTokenSource();
ConcurrentExclusiveSchedulerPair pair = new ConcurrentExclusiveSchedulerPair(TaskScheduler.Current);
Task Task1 = Task.Factory.StartNew((Func<object,Task>)doThing, 1, source.Token, TaskCreationOptions.AttachedToParent, pair.ExclusiveScheduler).Unwrap();
Task Task2 = Task.Factory.StartNew((Func<object, Task>)doThing, 2, source.Token, TaskCreationOptions.AttachedToParent, pair.ExclusiveScheduler);
Task Task3 = doThing(3);
Task Task4 = Task.Factory.StartNew(async (i) =>
{
Console.WriteLine("in do thing {0}", (int)i);
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("out of do thing {0}", (int)i);
}, 4, source.Token, TaskCreationOptions.None, pair.ExclusiveScheduler);
Task.WaitAll(Task1, Task2, Task3, Task4);
Console.ReadKey();
return;
}
TPL TaskSchedulers only can see one synchronous segment of an async method at a time, so you can't do it simply with schedulers. But you can do it, using a higher level primitive. One that I frequently use is TPL Dataflow.
First, install the NuGet package:
Install-Package Microsoft.Tpl.Dataflow
Then use this code:
private static async Task doThing(object i) {
Console.WriteLine("in do thing {0}", (int)i);
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("out of do thing {0}", (int)i);
}
static void Main(string[] args) {
CancellationTokenSource source = new CancellationTokenSource();
var exclusivityBlock = new ActionBlock<Func<Task>>(f => f(), new ExecutionDataflowBlockOptions { CancellationToken = source.Token }};
exclusivityBlock.Post(() => doThing(1));
exclusivityBlock.Post(() => doThing(2));
exclusivityBlock.Post(() => doThing(3));
exclusivityBlock.Post(
async () => {
Console.WriteLine("in do thing {0}", 4);
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("out of do thing {0}", 4);
});
exclusivityBlock.Complete();
exclusivityBlock.Completion.Wait();
Console.WriteLine("Done");
Console.ReadKey();
return;
}
This code lacks an individual Task for each posted work item. If that's important, you can use this sample:
internal static class Program {
private static async Task doThing(object i) {
Console.WriteLine("in do thing {0}", (int)i);
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("out of do thing {0}", (int)i);
}
private static void Main(string[] args) {
CancellationTokenSource source = new CancellationTokenSource();
var exclusivityBlock = CreateTrackingBlock<Func<Task>>(
f => f(), new ExecutionDataflowBlockOptions { CancellationToken = source.Token });
var task1 = exclusivityBlock.PostWithCompletion(() => doThing(1));
var task2 = exclusivityBlock.PostWithCompletion(() => doThing(2));
var task3 = exclusivityBlock.PostWithCompletion(() => doThing(3));
var task4 = exclusivityBlock.PostWithCompletion(
async () => {
Console.WriteLine("in do thing {0}", 4);
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("out of do thing {0}", 4);
});
Task.WaitAll(task1, task2, task3, task4);
Console.WriteLine("Done");
Console.ReadKey();
return;
}
private static ActionBlock<Tuple<T, TaskCompletionSource<object>>> CreateTrackingBlock<T>(Func<T, Task> action, ExecutionDataflowBlockOptions options = null) {
return new ActionBlock<Tuple<T, TaskCompletionSource<object>>>(
async tuple => {
try {
await action(tuple.Item1);
tuple.Item2.TrySetResult(null);
} catch (Exception ex) {
tuple.Item2.TrySetException(ex);
}
},
options ?? new ExecutionDataflowBlockOptions());
}
internal static Task PostWithCompletion<T>(this ActionBlock<Tuple<T, TaskCompletionSource<object>>> block, T value) {
var tcs = new TaskCompletionSource<object>();
var tuple = Tuple.Create(value, tcs);
block.Post(tuple);
return tcs.Task;
}
}
Note however that this is just a bit more laborious, because Dataflow isn't primarily designed for tracking individual submissions but rather the overall process. So while the above works just fine, Stephen Cleary's answer is probably simpler and therefore preferable.
Given the following code, is it possible to define scheduler, creation, and continuation settings for instances of Task doThing?
The bad news is: no, there is no way to do that. It doesn't make sense to define a "scheduler" for a non-lambda task. Creation options aren't needed, and continuation options are set on continuations, not on the task itself.
The good news is: you don't need this behavior.
You want asynchronous synchronization. The built-in way to do this is using SemaphoreSlim, as such:
SemaphoreSlim mutex = new SemaphoreSlim(1);
private static async Task doThingAsync(object i)
{
await mutex.WaitAsync();
try
{
Console.WriteLine("in do thing {0}", (int)i);
await Task.Delay(TimeSpan.FromSeconds(5));
Console.WriteLine("out of do thing {0}", (int)i);
}
finally
{
mutex.Release();
}
}
Personally, I think the finally syntax is awkward, so I define an IDisposable and use using instead.
If you need more power, Stephen Toub has an async coordination primitives series, and I have a full suite of primitives in my AsyncEx library. Both of these resources include an AsyncLock with a Task<IDisposable> WaitAsync() member so you can use using instead of finally.