I was looking at someone sample code for async and noticed a few issues with the way it was implemented. Whilst looking at the code I wondered if it would be more efficient to loop through a list using as parallel, rather than just looping through the list normally.
As far as I can tell there is very little difference in performance, both use up every processor, and both talk around the same amount of time to completed.
This is the first way of doing it
var tasks= Client.GetClients().Select(async p => await p.Initialize());
And this is the second
var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
Am I correct in assuming there is no difference between the two?
The full program can be found below
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication2 { class Program { static void Main(string[] args) { RunCode1(); Console.WriteLine("Here"); Console.ReadLine(); RunCode2(); Console.WriteLine("Here"); Console.ReadLine(); } private async static void RunCode1() { Stopwatch myStopWatch = new Stopwatch(); myStopWatch.Start(); var tasks= Client.GetClients().Select(async p => await p.Initialize()); Task.WaitAll(tasks.ToArray()); Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds); myStopWatch.Stop(); } private async static void RunCode2() { Stopwatch myStopWatch = new Stopwatch(); myStopWatch.Start(); var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize()); Task.WaitAll(tasks.ToArray()); Console.WriteLine("Time ellapsed(ms): " + myStopWatch.ElapsedMilliseconds); myStopWatch.Stop(); } } class Client { public static IEnumerable<Client> GetClients() { for (int i = 0; i < 100; i++) { yield return new Client() { Id = Guid.NewGuid() }; } } public Guid Id { get; set; } //This method has to be called before you use a client //For the sample, I don't put it on the constructor public async Task Initialize() { await Task.Factory.StartNew(() => { Stopwatch timer = new Stopwatch(); timer.Start(); while(timer.ElapsedMilliseconds<1000) {} timer.Stop(); }); Console.WriteLine("Completed: " + Id); } } }
The async keyword turns a method into an async method, which allows you to use the await keyword in its body. When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete. await can only be used inside an async method.
AsParallel(IEnumerable) Enables parallelization of a query. AsParallel<TSource>(Partitioner<TSource>) Enables parallelization of a query, as sourced by a custom partitioner that is responsible for splitting the input sequence into partitions.
The await operator is used to wait for a Promise . It can only be used inside an async function within regular JavaScript code; however it can be used on its own with JavaScript modules.
The differences between asynchronous and synchronous include: Async is multi-thread, which means operations or programs can run in parallel. Sync is single-thread, so only one operation or program will run at a time. Async is non-blocking, which means it will send multiple requests to a server.
There should be very little discernible difference.
In your first case:
var tasks = Client.GetClients().Select(async p => await p.Initialize());
The executing thread will (one at a time) start executing Initialize
for each element in the client list. Initialize
immediately queues a method to the thread pool and returns an uncompleted Task
.
In your second case:
var tasks = Client.GetClients().AsParallel().Select(async p => await p.Initialize());
The executing thread will fork to the thread pool and (in parallel) start executing Initialize
for each element in the client list. Initialize
has the same behavior: it immediately queues a method to the thread pool and returns.
The two timings are nearly identical because you're only parallelizing a small amount of code: the queueing of the method to the thread pool and the return of an uncompleted Task
.
If Initialize
did some longer (synchronous) work before its first await
, it may make sense to use AsParallel
.
Remember, all async
methods (and lambdas) start out being executed synchronously (see the official FAQ or my own intro post).
There's a singular major difference.
In the following code, you are taking it upon yourself to perform the partitioning. In other words, you're creating one Task
object per item from the IEnumerable<T>
that is returned from the call to GetClients()
:
var tasks= Client.GetClients().Select(async p => await p.Initialize());
In the second, the call to AsParallel
is internally going to use Task
instances to execute partitions of the IEnumerable<T>
and you're going to have the initial Task
that is returned from the lambda async p => await p.Initialize()
:
var tasks = Client.GetClients().AsParallel(). Select(async p => await p.Initialize());
Finally, you're not really doing anything by using async
/await
here. Granted, the compiler might optimize this out, but you're just waiting on a method that returns a Task
and then returning a continuation that does nothing back through the lambda. That said, since the call to Initialize
is already returning a Task
, it's best to keep it simple and just do:
var tasks = Client.GetClients().Select(p => p.Initialize());
Which will return the sequence of Task
instances for you.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With