I have the following C# code :
var rand = new Random(1); var range = Enumerable.Range(1, 8); var partition = Partitioner.Create(range, EnumerablePartitionerOptions.NoBuffering); foreach (var x in partition .AsParallel() .AsOrdered() .WithMergeOptions(ParallelMergeOptions.NotBuffered) .WithDegreeOfParallelism(4) .Select(DoSomething)) { Console.WriteLine($"---- {x} {DateTime.Now.TimeOfDay} " + $"{Thread.CurrentThread.ManagedThreadId}"); } int DoSomething(int x) { Console.WriteLine($"WAIT {x} {DateTime.Now.TimeOfDay} " + $"{Thread.CurrentThread.ManagedThreadId}"); int random; lock (rand) { random = rand.Next(2000); } Thread.Sleep(random); //fake work being done Console.WriteLine($"DONE {x} {DateTime.Now.TimeOfDay} " + $"{Thread.CurrentThread.ManagedThreadId}"); return x; } Here is the output :
WAIT 2 13:51:08.8398170 10 WAIT 1 13:51:08.8398197 9 WAIT 3 13:51:08.8398132 11 WAIT 4 13:51:08.8398108 4 DONE 1 13:51:09.0805471 9 <-- start WAIT 5 13:51:09.0808715 9 DONE 2 13:51:09.3504889 10 WAIT 6 13:51:09.3505787 10 DONE 3 13:51:09.7937364 11 WAIT 7 13:51:09.7939256 11 DONE 6 13:51:10.2208844 10 WAIT 8 13:51:10.2209660 10 DONE 4 13:51:10.3948195 4 ---- 1 13:51:10.3951458 2 <-- end DONE 5 13:51:10.4109264 9 ---- 2 13:51:10.4112009 2 ---- 3 13:51:10.4112443 2 DONE 7 13:51:10.5068458 11 ---- 4 13:51:10.5068961 2 ---- 5 13:51:10.5071686 2 ---- 6 13:51:10.5072167 2 DONE 8 13:51:12.1163565 10 ---- 7 13:51:12.1164506 2 ---- 8 13:51:12.1165087 2 As you can see, there is 1.3 seconds that elapsed between the moment item 1 is being processed and the moment it get printed out to console.
Because I explicitly asked for no buffering (neither in the partitioning and in the processing of the results), I would have expected the item 1 to be printed out in the foreach loop as soon as that item is processed (which is when it left DoSomething method). Is there some options I'm missing or is this expected behavior ?
Thread.Sleep(random); //fake work being donedoesn't fake work, it wastes the ThreadPool thread. When you callThread.Sleepthe thread is evicted immediately and will have to be scheduled for execution whenever the OS decides after it wakes up.there is nothing that prevent or block the main thread to output things to consolewrong. That thread is used to process data itself. I suspect you misunderstand how PLINQ or evenParallel.ForEachwork. Both are blocking operations. Besides, thatforeachis sequential and blocking, which means it can only proceed when the query produces output. And the query can't proceed because all threads are evictedSelectthat doesn't select is a bug in itself. Parallel LINQ isn't a way of running things in parallel. It's built to process lots of data in parallel. It's specifically built to parallelize the entire query, not just the last, supposedly cheapest, part. It's built to create a pipeline of operators and use operators optimized for parallelism for all of them. Partitioning is used to reduce the need for synchronization between workers. The iteration is where the results are finally collected. By usingAsOrdered()though you're forcing extra sync and buffering at the end