PLinq不需要鎖的情況

惊惊發表於2024-11-11

在看十億行天文臺資料挑戰的時候發現不需要鎖,
研究之下發現,即使並行執行,也不一定需要鎖

Aggregate它本身是並行的,
同執行緒內就順序執行字典了,
非同執行緒會等待到結束並行之後再合併,
這樣是不需要鎖的.

如果不是末尾,那麼需要再次.AsParallel

var partialSums = numbers.AsParallel()
    .Select(n => n * 2)
    .Aggregate(0, (acc, n) => acc + n)
    .AsParallel()
    .Select(n => n / 2);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

class Program
{
    static void Main()
    {
        // 建立一個字串列表
        List<string> words = new List<string> { "hello", "world", "linq", "parallel", "example" };

        // 使用PLINQ並行處理列表
        var uppercasedWords = words.AsParallel().Select(word => {
            // 模擬一些處理,這裡只是簡單地將字串轉換為大寫
            return word.ToUpper();
        });

        // 順序輸出結果
        foreach (var word in uppercasedWords)
        {
            Console.WriteLine(word);
        }
    }
}
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        // 建立一個數字列表
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

        // 使用PLINQ並行處理列表
        var processedNumbers = numbers.AsParallel()
            .Select(n => n * 2) // 將每個數字翻倍
            .Where(nn => nn % 2 == 0) // 篩選出偶數
            .Select(x => x / 2); // 將篩選後的數字除以2

        // 順序輸出結果
        foreach (var number in processedNumbers)
        {
            Console.WriteLine(number);
        }
    }
}
var result = numbers.AsParallel()
    .Select(n => n * 2)
    .Aggregate(0, (acc, n) => acc + n);

在這個例子中,  
.Aggregate()  方法會在  
.Select(n => n * 2)  操作完成後開始執行,
並在所有元素處理完畢後合併結果。

.Aggregate()  方法本身是順序執行的,
它會在所有元素被處理後,順序地合併中間結果。

如果需要在並行環境中安全地合併結果,
並且希望合併操作也是並行的,
那麼可能需要考慮使用  .AsSequential()  
來將後續操作轉換為順序執行,
或者使用  .Aggregate()  的執行緒安全版本,
如  .AsParallel().AsOrdered().Aggregate()  ,
或者使用鎖來保護共享資源。
using System;
using System.Collections.Generic;
using System.Linq;

class Order
{
    public int OrderId { get; set; }
    public double TotalAmount { get; set; }
    // 假設這是一個非執行緒安全的方法,需要順序呼叫
    public void UpdateDatabaseRecord()
    {
        // 更新資料庫記錄的邏輯
        Console.WriteLine($"Order {OrderId} updated in database.");
    }
}

class Program
{
    static void Main()
    {
        // 假設這是從資料庫載入的訂單列表
        List<Order> orders = Enumerable.Range(1, 1000)
.Select(i => new Order { OrderId = i, TotalAmount = 100.0 * i })
.ToList();

        // 使用AsParallel啟動並行查詢
        var parallelQuery = orders.AsParallel();

        // 並行執行一些操作,比如篩選和投影
        var largeOrders = parallelQuery
.Where(o => o.TotalAmount > 500)
.Select(o => new { o.OrderId, o.TotalAmount });

        // 在這一點,我們需要順序執行操作,因為UpdateDatabaseRecord是非執行緒安全的
        largeOrders.AsSequential().ForEach(order =>
        {
            order.UpdateDatabaseRecord(); // 順序更新每個訂單的資料庫記錄
        });
    }
}

相關文章