Task.Factory.StartNew with async lambda and Task.WaitAll
Task.Factory.StartNew
doesn't recognise async
delegates as there is no overload that accepts a function returning a Task
.
This plus other reasons (see StartNew is dangerous) is why you should be using Task.Run
here:
tasks.Add(Task.Run(async () => ...
Task Factory for each loop with await
This is a typical problem that C# 8.0 Async Streams are going to solve very soon.
Until C# 8.0 is released, you can use the AsyncEnumarator library:
using System.Collections.Async;
class Program
{
private async Task SQLBulkLoader() {
await indicators.file_list.ParallelForEachAsync(async fileListObj =>
{
...
await s.WriteToServerAsync(dataTableConversion);
...
},
maxDegreeOfParalellism: 3,
cancellationToken: default);
}
static void Main(string[] args)
{
Program worker = new Program();
worker.SQLBulkLoader().GetAwaiter().GetResult();
}
}
I do not recommend using Parallel.ForEach
and Task.WhenAll
as those functions are not designed for asynchronous streaming.
Correctly awaiting Task.Run with async lambda expression
Your code will wait till the end of the compute using the task mechanism. This will not block your thread but it will not execute code beyond this point until everything in your loop is done.
public static async Task Test()
{
await Task.Run(async () =>
{
for (int i = 0; i < 10; i++)
{
await Task.Delay(TimeSpan.FromSeconds(1));
Console.WriteLine($"inside {i}");
}
});
Console.WriteLine("Done");
}
will give the following output:
inside 0
inside 1
inside 2
inside 3
inside 4
inside 5
inside 6
inside 7
inside 8
inside 9
Done
How to wait until all the tasks finish before returning from method in C#
When all can be used to create a new task which completes when all tasks in it completes, you can then proceed fetching results out of that task using the regular async patterns.
https://msdn.microsoft.com/en-us/library/system.threading.tasks.task.whenall(v=vs.110).aspx
So
//If your validation results are strings:
Task[] tasks = ...
var things = await Task.WhenAll(tasks);
Alternatively WaitAll can be used, but if you need to do work on the output that is an awkward aproach here. IMO staying in the async context is always better anyways.
Task.WaitAll(tasks);
foreach(var t in tasks) DoSomething(t.Result);
https://msdn.microsoft.com/en-us/library/dd270695(v=vs.110).aspx
Besides, it blocks the current thread AFAIK.
A more complete console example:
https://dotnetfiddle.net/JMLHxR
using System;
using System.Threading.Tasks;
public class Program
{
private static int counter = 0;
public static void Main()
{
Random rnd = new Random();
var tasks = new Task<object>[]
{
// If your target method returns a task.
// - note: A Proxy or similar approach will probably be more readable.
CreateTask(rnd.Next(500,2000)).ContinueWith(t => (object)t.Result),
CreateOtherTask(rnd.Next(500,2000)).ContinueWith(t => (object)t.Result),
CreateTask(rnd.Next(500,2000)).ContinueWith(t => (object)t.Result),
CreateOtherTask(rnd.Next(500,2000)).ContinueWith(t => (object)t.Result),
//If your target method is syncronious.
Task.Run(() => (object)CreateSimple()),
Task.Run(() => (object)CreateMessage()),
Task.Run(() => (object)CreateSimple()),
Task.Run(() => (object)CreateMessage())
};
Task.WaitAll(tasks);
foreach(var t in tasks)
Console.WriteLine(t.Result);
// They are already completed here, but just to show the syntax.
// .Result should obvisously be awaited instead.
var all = Task.WhenAll(tasks).Result;
foreach(var result in all)
Console.WriteLine(result);
}
private static string CreateSimple()
{
int id = Program.counter++;
return "Task [" + id + "] delayed: NONE";
}
private static Message CreateMessage()
{
return new Message(CreateSimple());
}
private static async Task<string> CreateTask(int delay)
{
int id = Program.counter++;
await Task.Delay(delay);
return "Task [" + id + "] delayed: " + delay;
}
private static async Task<Message> CreateOtherTask(int delay)
{
int id = Program.counter++;
await Task.Delay(delay);
return new Message("Task [" + id + "] delayed: " + delay);
}
public class Message {
private string message;
public Message(string msg) { message = msg; }
public override string ToString(){ return message; }
}
}
Task.WaitAll not waiting for task to complete
You should avoid using Task.Factory.StartNew
with async-await. You should use Task.Run
instead.
An async method returns a Task<T>
, an async delegate does as well. Task.Factory.StartNew
also returns a Task<T>
, where its result is the result of the delegate parameter. So when used together it returns a Task<Task<T>>>
.
All that this Task<Task<T>>
does is execute the delegate until there's a task to return, which is when the first await is reached. If you only wait for that task to complete you aren't waiting for the whole method, just the part before the first await.
You can fix that by using Task.Unwrap
which creates a Task<T>
that represents that Task<Task<T>>>
:
Task<Task> wrapperTask = Task.Factory.StartNew(...);
Task actualTask = wrapperTask.Unwrap();
Task.WaitAll(actualTask);
Task.Factory.StartNew with async lambda and Task.WaitAll
Task.Factory.StartNew
doesn't recognise async
delegates as there is no overload that accepts a function returning a Task
.
This plus other reasons (see StartNew is dangerous) is why you should be using Task.Run
here:
tasks.Add(Task.Run(async () => ...
How to wait on Async tasks
So there are several separate bugs here.
First, for Execute
, you're using StartNew
with an async
lambda. Since StartNew
doesn't have a Task<Task>
returning overload, like Task.Run
does, you've got a method that returns a Task
indicating when the asynchronous operation has finished starting, not when the asynchronous operation has finished, which means that the Task
returned by Execute
will be completed basically right away, rather than after Delay
finishes or the action you call finishes. Additionally, there's simply no reason to use StartNew
or Run
at all when running asynchronous methods, you can just execute them normally and await
them without pushing them to a thread pool thread.
Next, Execute
accepts an Action
, which implies that it's a synchronous method that doesn't compute any value. What you're providing is an asynchronous method, but as the delegate doesn't return a Task
, Execute
can't await
it. If you want Execute
to handle asynchronous methods, it needs to accept a delegate that returns a Task
.
So given all of that Execute
should look like this.
private static async Task Execute(Func<Task> action)
{
await Task.Delay(TimeSpan.FromMilliseconds(5));
await action();
}
Next onto the Main
method. As mentioned before Execute
is accepting an Action
when you're trying to provide an async
method. This means that when the action is run the code will continued executing before your actions have finished. You need to adjust it to using a Task
returning method.
After all of that, your code still has a race condition in it, at a conceptual level, that will prevent you from theoretically getting the results in the right order. You're performing 3 different operations in parallel, and as a result of that, they can finish in any order. While you are atomically incrementing the counter, it's possible for one thread to increment the counter, then another to run, increment the counter, print its value, then have the other thread run again and print out the value, given you a possible output of what you have, even after fixing all of the bugs mentioned above. To ensure that the values are printed in order, you need to ensure that the increment and the console write are performed atomically.
Now you can write out your Main
method like so:
int ExecutionCounter = 0;
object key = new object();
Func<Task> myAction = async () =>
{
await Task.Delay(TimeSpan.FromMilliseconds(5));
lock (key)
{
Console.WriteLine(++ExecutionCounter);
}
};
var actions = new[] { myAction, myAction, myAction };
Task.WaitAll(actions.Select(a => Execute(a)).ToArray()); //This blocks, right?
And yes, as your comment mentions, calling WaitAll
will block, rather than being asynchronous.
Generate ListTask with using index in for like parameter
There are a few issues here.
To directly answer your question, you need to create a closure over i
to prevent it being updated before it is accessed in your async code.
You can do this by swapping your for
loop with Enumerable.Range
.
Another issue is that you are running GetValueAsync
on the ThreadPool but not awaiting it. Subsequently, your Task.WaitAll
will be waiting for the outer Task
s only, not the Task
s returned by GetValueAsync
.
Here is an example of what you could do:
var tasks = Enumerable.Range(0, valueSize)
.Select(async i =>
{
string val = await _tcpRepository.GetValueAsync(i);
Values[i] = val.GetInt32();
});
Your final issue is the use of Task.WaitAll
; this introduces the sync-over-async antipattern. You should allow async to grow through your code base, and instead use Task.WhenAll
, which returns a Task
that completes when all the provided Task
s are complete:
await Task.WhenAll(tasks);
Related Topics
Different Forms of the Wcf Service Contract Interface
System.Drawing in Windows or ASP.NET Services
No Database Provider Has Been Configured for This Dbcontext' on Signinmanager.Passwordsigninasync
Best Practice: Convert Linq Query Result to a Datatable Without Looping
How Can a Windows Service Determine Its Servicename
Adding Elements to an Xml File in C#
Performance Cost of 'Try' in C#
Closing a File After File.Create
Asynchronous Iterator Task<Ienumerable<T>>
Programmatic Way to Get All the Available Languages (In Satellite Assemblies)
Reasons for Why a Winforms Label Does Not Want to Be Transparent
Linq Query Group by and Selecting First Items
Can Someone Explain How Bcrypt Verifies a Hash
How to Format Timespan in Xaml
How to Call the Parent Version of an Overridden Method? (C# .Net)