我有一個簡單的程序,它遍歷作爲反饋枚舉器實現的無盡枚舉。我已經在TPL和PLINQ中實現了這一點。這兩個示例都在可預測的迭代次數後鎖定:PLINQ爲8,TPL爲3。它的代碼在不使用TPL/PLINQ的情況下執行,運行良好。我已經以非線程安全的方式以及線程安全的方式實現了枚舉器。如果並行度限制爲1(如示例中的情況),則可以使用前者。非線程安全的枚舉器非常簡單,不依賴任何'花哨的'.NET庫類。如果我增加並行度,則在死鎖之前執行的迭代次數增加,例如對於PLINQ,迭代次數爲8 *並行度。循環枚舉的PLINQ迭代導致死鎖
下面是迭代器:
枚舉(非線程)
public class SimpleEnumerable<T>: IEnumerable<T>
{
private T _value;
private readonly AutoResetEvent _releaseValueEvent = new AutoResetEvent(false);
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
while(true)
{
_releaseValueEvent.WaitOne();
yield return _value;
}
}
public void OnNext(T value)
{
_value = value;
_releaseValueEvent.Set();
}
}
枚舉(線程)
public class SimpleEnumerable<T>: IEnumerable<T>
{
private readonly BlockingCollection<T> _blockingCollection = new BlockingCollection<T>();
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
while(true)
{
yield return _blockingCollection.Take();
}
}
public void OnNext(T value)
{
_blockingCollection.Add(value);
}
}
PLINQ實施例:
public static void Main(string[] args)
{
var enumerable = new SimpleEnumerable<int>();
enumerable.OnNext(0);
enumerable
.Do(i => Debug.WriteLine($"{i} {Thread.CurrentThread.ManagedThreadId}"))
.AsParallel()
.WithDegreeOfParallelism(1)
.ForEach
(
i =>
{
Debug.WriteLine($"{i} {Thread.CurrentThread.ManagedThreadId}");
enumerable.OnNext(i+1);
}
);
}
TPL實例:在我的調用堆棧的分析
public static void Main(string[] args)
{
var enumerable = new SimpleEnumerable<int>();
enumerable.OnNext(0);
Parallel.ForEach
(
enumerable,
new ParallelOptions { MaxDegreeOfParallelism = 1},
i =>
{
Debug.WriteLine($"{i} {Thread.CurrentThread.ManagedThreadId}");
enumerable.OnNext(i+1);
}
);
}
基地,似乎存在着發生在無論是在PLINQ和TPL一個分區相關方法的僵局,但我不是確定如何解釋這一點。
通過試驗和錯誤我發現包裝PLINQ enumerable
在Partitioner.Create(enumerable, EnumerablePartitionerOptions.NoBuffering)
修復了問題,但我不知道爲什麼發生死鎖。
我會很有興趣找出錯誤的根源。
請注意,這是一個人爲的例子。我不是在尋找批評的代碼,而是爲什麼是發生死鎖。具體而言,在PLINQ示例中,如果.AsParallel()
和.WithDegreeOfParallelism(1)
行被註釋掉,則代碼工作得很好。
PLINQ和並行不會導致死鎖,他們使用當前線程與其他人並行處理數據 –
@PanagiotisKanavos顯然,死鎖是在他的迭代器中。乍一看,我並不感到驚訝,它肯定看起來並不安全。 – Servy
@Servy我從最明顯的問題開始。迭代器...是一個非常非常奇怪的結構。一個簡單的10K數組將足以測試並行執行。 'Interlocked.Increment'將是一個非常好的方法來保持計數。這個迭代器只是阻止了 –