我的_baseBlockContainer.GetBaseBlocks();回傳ConcurrentQueue帶有15317物件。為了進一步處理,我想對它們進行排序Type。但是,它總是“錯過”一些物件。
似乎 myParallel.ForEach不是執行緒安全的,因為ConcurrentQueuefor a中的物件數量Type有時Type比按同步排序時少(對于 a 減少 1 到 250 個物件)foreach;但我看不出在哪里/為什么。
var baseBlocks = _baseBlockContainer.GetBaseBlocks();
var baseBlocksByTypeConcurrent = new ConcurrentDictionary<Type, ConcurrentQueue<BaseBlock>>();
// results of this always differ
Parallel.ForEach(baseBlocks, bb =>
{
if (!baseBlocksByTypeConcurrent.ContainsKey(bb.GetType()))
{
baseBlocksByTypeConcurrent[bb.GetType()] = new ConcurrentQueue<BaseBlock>();
}
baseBlocksByTypeConcurrent[bb.GetType()].Enqueue(bb);
});
var baseBlocksByType = new ConcurrentDictionary<Type, ConcurrentQueue<BaseBlock>>();
// results of this are always the same
foreach (var bb in baseBlocks)
{
if (!baseBlocksByType.ContainsKey(bb.GetType()))
{
baseBlocksByType[bb.GetType()] = new ConcurrentQueue<BaseBlock>();
}
baseBlocksByType[bb.GetType()].Enqueue(bb);
}
uj5u.com熱心網友回復:
替換這個:
if (!baseBlocksByTypeConcurrent.ContainsKey(bb.GetType()))
{
baseBlocksByTypeConcurrent[bb.GetType()] = new ConcurrentQueue<BaseBlock>();
}
baseBlocksByTypeConcurrent[bb.GetType()].Enqueue(bb);
有了這個:
baseBlocksByTypeConcurrent.TryAdd(bb.GetType(), new ConcurrentQueue<BaseBlock>());
baseBlocksByTypeConcurrent[bb.GetType()].Enqueue(bb);
您現有代碼的問題是,如果同時在多個執行緒中對同一塊型別.ContainsKey進行評估false,那么它們都會將與該型別對應的值設定為新佇列,從而擦除該型別的任何現有佇列。也就是說:ContainsKey索引器本身是執行緒安全的,但在以您的方式單獨使用時卻不是。
TryAdd是執行緒安全的,并且只會添加一次該鍵,而不是像分配給索引器那樣重寫它。
uj5u.com熱心網友回復:
您的代碼正遭受所謂的競爭條件。單獨使用并發集合并不能防止競爭條件的發生。您還必須通過使用它們特殊的原子 API 來正確使用它們。在您的情況下,要使用的適當 API 是GetOrAdd方法:
ConcurrentDictionary<TKey,TValue>如果鍵不存在,則將鍵/值對添加到。如果鍵已經存在,則回傳新值或現有值。
用法:
ParallelOptions options = new()
{
MaxDegreeOfParallelism = Environment.ProcessorCount
};
Parallel.ForEach(baseBlocks, options, bb =>
{
baseBlocksByTypeConcurrent
.GetOrAdd(bb.GetType(), _ => new ConcurrentQueue<BaseBlock>())
.Enqueue(bb);
});
作為旁注,每當您使用該Parallel.ForEach方法時,建議明確指定MaxDegreeOfParallelism. 默認MaxDegreeOfParallelism值為 -1,這意味著無限并行性,實際上會使ThreadPool.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/483526.html
標籤:C# 并发 并行处理 并行.foreach
上一篇:通用基類建構式
