如何避免C# Parallel.ForEach的競(jìng)態(tài)條件

c#
小樊
81
2024-10-09 12:43:44

要避免 C# 中的 Parallel.ForEach 競(jìng)態(tài)條件,您需要確保在并行操作期間對(duì)共享資源的訪問(wèn)是線程安全的。這可以通過(guò)以下幾種方式來(lái)實(shí)現(xiàn):

  1. 使用鎖(Locks):在執(zhí)行對(duì)共享資源的操作時(shí),使用 lock 語(yǔ)句確保一次只有一個(gè)線程可以訪問(wèn)資源。
object lockObject = new object();
Parallel.ForEach(items, item => {
    lock (lockObject) {
        // 訪問(wèn)共享資源的代碼
    }
});
  1. 使用線程安全的數(shù)據(jù)結(jié)構(gòu):例如 ConcurrentQueue<T>、ConcurrentBag<T>BlockingCollection<T> 等,這些數(shù)據(jù)結(jié)構(gòu)在內(nèi)部實(shí)現(xiàn)了線程安全機(jī)制。
ConcurrentQueue<int> queue = new ConcurrentQueue<int>();
Parallel.ForEach(items, item => {
    queue.Enqueue(item);
});
  1. 使用原子操作:對(duì)于簡(jiǎn)單的數(shù)值操作,可以使用 Interlocked 類提供的原子操作方法,如 Interlocked.IncrementInterlocked.Decrement。
int counter = 0;
Parallel.ForEach(items, item => {
    Interlocked.Increment(ref counter);
});
  1. 使用分區(qū):將數(shù)據(jù)分成多個(gè)部分,每個(gè)部分在不同的線程中處理。這樣可以減少對(duì)共享資源的競(jìng)爭(zhēng)。
int numOfPartitions = Environment.ProcessorCount;
var partitions = Partitioner.Create(items, numOfPartitions);
Parallel.ForEach(partitions, partition => {
    foreach (var item in partition) {
        // 處理每個(gè)分區(qū)的代碼
    }
});
  1. 避免全局狀態(tài):盡量減少全局狀態(tài)的使用,因?yàn)樗赡軐?dǎo)致競(jìng)態(tài)條件。如果必須使用全局狀態(tài),請(qǐng)確保對(duì)其訪問(wèn)進(jìn)行同步。

  2. 使用 Parallel LINQ (PLINQ):PLINQ 可以讓您以聲明式方式編寫并行代碼,它會(huì)自動(dòng)處理并行性和對(duì)共享資源的訪問(wèn)。

var result = items.AsParallel().Where(item => {
    // 過(guò)濾條件
}).ToList();

總之,要避免 Parallel.ForEach 的競(jìng)態(tài)條件,關(guān)鍵是確保對(duì)共享資源的訪問(wèn)是線程安全的。您可以使用鎖、線程安全的數(shù)據(jù)結(jié)構(gòu)、原子操作、分區(qū)等方法來(lái)實(shí)現(xiàn)這一目標(biāo)。

0