問題 有一批數據,需要對每個元素進行相同的操作。該操作是計算密集型的,需要耗費一定的時間。 解決方案 常見的操作可以粗略分為 計算密集型操作 和 IO密集型操作。計算密集型操作主要是依賴於CPU計算,所以可以最大限度利用多核CPU的並行操作非常適合計算密集型操作。圖像操作是比較常見的計算密集型操作, ...
問題
有一批數據,需要對每個元素進行相同的操作。該操作是計算密集型的,需要耗費一定的時間。
解決方案
常見的操作可以粗略分為 計算密集型操作 和 IO密集型操作。計算密集型操作主要是依賴於CPU計算,所以可以最大限度利用多核CPU的並行操作非常適合計算密集型操作。圖像操作是比較常見的計算密集型操作,圖像操作一般是藉助矩陣存儲圖像數據,該書作者就舉了矩陣旋轉為例。
思路是藉助Parallel.ForEach實現並行操作。
偽代碼如下
void RotateMatrices(IEnumerable<Matrix> matrices, float degrees)
{
Parallel.ForEach(matrices, matrix => matrix.Rotate(degrees));
}
當前迴圈處理無效值時,可能需要停止該迴圈,實現如下
void InvertMatrices(IEnumerable<Matrix> matrices)
{
Parallel.ForEach(matrices, (matrix, state) =>
{
if (!matrix.IsInvertible)
state.Stop();
else
matrix.Invert();
});
}
而如果是想要取消整個並行迴圈,則需要藉助CancellationToken,即可以有一個取消按鈕,點擊取消按鈕,應取消迴圈操作。
void RotateMatrices(IEnumerable<Matrix> matrices, float degrees,
CancellationToken token)
{
Parallel.ForEach(matrices,
new ParallelOptions { CancellationToken = token },
matrix => matrix.Rotate(degrees));
}
另:當一個全局變數在並行迴圈內部被操作時,則需要考慮多進程共用狀態。因為並行迴圈的每個迴圈很可能在不同的線程中運行。
// 註意,這不是最高效的實現方式。
// 只是舉個例子,說明用鎖來保護共用狀態。
int InvertMatrices(IEnumerable<Matrix> matrices)
{
object mutex = new object();
int nonInvertibleCount = 0;
Parallel.ForEach(matrices, matrix =>
{
if (matrix.IsInvertible)
{
matrix.Invert();
}
else
{
lock (mutex)
{
++nonInvertibleCount;
}
}
});
return nonInvertibleCount;
}
在 《C#併發編程經典實例》學習筆記-第一章併發編程概述 - repeatedly - 博客園 ,我提到了並行操作的兩種方式,一種是Parallel,另一種是PLINQ(Parallel LINQ)。所以上述操作也可以使用PLINQ實現。
兩者是有區別的,區別如下:
Parallel 類和 PLINQ 之間有一個區別:PLINQ 假設可以使用電腦內所有的CPU 核,而 Parallel 類則會根據 CPU 狀態的變化動態地調整。
相信對C#語法比較熟悉的應該能看出來,Parallel.ForEach是並行foreach迴圈,那麼並行for迴圈對應的方法是什麼呢?是Parallel.For 方法。