[C#] Keeping Data Order in Parallel Processing with PLINQ (AsOrdered)

When you parallelize data processing using Parallel LINQ (PLINQ), performance is prioritized by default. This means the order of the results will likely differ from the input order (it becomes unordered).

However, for time-series data or ranked lists, the “order” itself is meaningful. In such cases, you may want to benefit from parallelization while ensuring the final output preserves the original sequence.

This article explains how to maintain input order during parallel processing using the AsOrdered extension method.

目次

Order-Preserving Parallel Queries with AsOrdered

By adding .AsOrdered() to your PLINQ query chain, the LINQ runtime buffers the results and reorders them based on their original index before outputting.

The sample code below assumes a scenario of analyzing server access logs recorded in chronological order. The logs must remain in order of occurrence, so we implement the logic to preserve this sequence even after parallel processing.

Sample Code

using System;
using System.Linq;
using System.Threading;

public class Program
{
    public static void Main()
    {
        // 1. Data Source: Chronological server access logs
        // Assuming they are sorted by ID (chronological order)
        var accessLogs = new[]
        {
            new { LogId = 1001, Path = "/api/v1/users", LatencyMs = 120 },
            new { LogId = 1002, Path = "/api/v1/auth",  LatencyMs = 350 },
            new { LogId = 1003, Path = "/home/index",   LatencyMs = 45 },
            new { LogId = 1004, Path = "/api/v1/data",  LatencyMs = 800 },
            new { LogId = 1005, Path = "/assets/img",   LatencyMs = 20 },
            new { LogId = 1006, Path = "/login",        LatencyMs = 150 },
        };

        Console.WriteLine("--- Analysis Results (Order Preserved) ---");

        // 2. Build PLINQ Query
        var analyzedLogs = accessLogs
            .AsParallel()                 // Enable parallelization
            .AsOrdered()                  // ★ Instruct to preserve input order
            .WithDegreeOfParallelism(4)   // Run with max 4 threads
            .Select(log =>
            {
                // Simulated analysis (Get Thread ID to confirm parallel execution)
                int threadId = Thread.CurrentThread.ManagedThreadId;
                
                // Logic to determine status
                string status = log.LatencyMs > 200 ? "SLOW" : "OK";
                
                return new
                {
                    log.LogId,
                    log.Path,
                    Result = status,
                    ProcessThread = threadId
                };
            });

        // 3. Output Results
        // Although Select runs on scattered threads,
        // the order LogId: 1001, 1002... is guaranteed in the foreach loop
        foreach (var item in analyzedLogs)
        {
            Console.WriteLine(
                $"ID:{item.LogId} [{item.Result,-4}] {item.Path} (Thread:{item.ProcessThread})"
            );
        }
    }
}

Explanation and Technical Points

1. The Role of AsOrdered

Normally, operations following .AsParallel() (such as Select or Where) output results as soon as they are ready, in no particular order. By inserting .AsOrdered(), PLINQ assigns an index to each element and guarantees that they are sorted back into their original order at the final output stage.

2. Performance Trade-off

Preserving order incurs a cost (overhead) because the runtime must temporarily buffer the parallel results and synchronize them for sorting. Therefore, processing speed will be slightly slower compared to a simple .AsParallel() (unordered). If the order is not important, leaving the default behavior (unordered) is better for performance.

3. Usage within a Query

The effect of AsOrdered also influences subsequent operators (like Take or Skip). For example, AsParallel().Take(10) retrieves “any 10 items that finished processing first,” whereas AsParallel().AsOrdered().Take(10) reliably retrieves the “first 10 items from the original list.”

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次