c# - 多线程访问和编辑同一个双精度数组
问题描述
我需要遍历数组中的每个双精度来执行“拉普拉斯平滑”,“混合值”与相邻双精度。
我会将存储的值保存在临时克隆数组中,最后更新原始值。
伪代码:
double[] A = new double[1000];
// Filling A with values...
double[] B = A.Clone as double[];
for(int loops=0;loops<10;loops++){ // start of the loop
for(int i=0;i<1000;i++){ // iterating through all doubles in the array
// Parallel.For(0, 1000, (i) => {
double v= A[i];
B[i]-=v;
B[i+1]+=v/2;
B[i-1]+=v/2;
// here i'm going out of array bounds, i know. Pseudo code, not relevant.
}
// });
}
A = B.Clone as double[];
使用for
它可以正常工作。“平滑”数组中的值。
我Parallel.For()
遇到了一些访问同步问题:线程正在发生冲突,并且某些值实际上没有正确存储。线程多次访问和编辑同一索引处的数组。
(我没有在线性数组中测试过这个,我实际上正在研究一个多维数组[x,y,z] ..)
我该如何解决这个问题?
我正在考虑为每个线程创建一个单独的数组,然后再进行求和......但我需要知道线程索引并且我在网络上的任何地方都没有找到。(即使使用完全不同的解决方案,如果存在“线程索引”,我仍然感兴趣......)。
我会接受任何解决方案。
解决方案
您可能需要该方法的更高级重载之一Parallel.For
:
public static ParallelLoopResult For<TLocal>(int fromInclusive, int toExclusive,
ParallelOptions parallelOptions, Func<TLocal> localInit,
Func<int, ParallelLoopState, TLocal, TLocal> body,
Action<TLocal> localFinally);
使用线程本地数据执行 for 循环,其中迭代可以并行运行,可以配置循环选项,并且可以监视和操作循环的状态。
对于它所期望的所有各种 lambda,这看起来非常令人生畏。这个想法是让每个线程处理本地数据,最后合并数据。以下是如何使用此方法来解决您的问题:
double[] A = new double[1000];
double[] B = (double[])A.Clone();
object locker = new object();
var parallelOptions = new ParallelOptions()
{
MaxDegreeOfParallelism = Environment.ProcessorCount
};
Parallel.For(0, A.Length, parallelOptions,
localInit: () => new double[A.Length], // create temp array per thread
body: (i, state, temp) =>
{
double v = A[i];
temp[i] -= v;
temp[i + 1] += v / 2;
temp[i - 1] += v / 2;
return temp; // return a reference to the same temp array
}, localFinally: (localB) =>
{
// Can be called in parallel with other threads, so we need to lock
lock (locker)
{
for (int i = 0; i < localB.Length; i++)
{
B[i] += localB[i];
}
}
});
我应该提一下,上述示例的工作负载过于细化,因此我不希望并行化能够大幅提高性能。希望您的实际工作量更大。例如,如果您有两个嵌套循环,则仅并行化外循环将非常有效,因为内循环将提供急需的块状。
替代解决方案:您可以直接更新 B 数组,而不是为每个线程创建辅助数组,并且仅在处理分区边界附近的危险区域中的索引时才使用锁:
Parallel.ForEach(Partitioner.Create(0, A.Length), parallelOptions, range =>
{
bool lockTaken = false;
try
{
for (int i = range.Item1; i < range.Item2; i++)
{
bool shouldLock = i < range.Item1 + 1 || i >= range.Item2 - 1;
if (shouldLock) Monitor.Enter(locker, ref lockTaken);
double v = A[i];
B[i] -= v;
B[i + 1] += v / 2;
B[i - 1] += v / 2;
if (shouldLock) { Monitor.Exit(locker); lockTaken = false; }
}
}
finally
{
if (lockTaken) Monitor.Exit(locker);
}
});
推荐阅读
- python-3.x - 从 Windows 上的命令提示符打开位于 google 文件流驱动器中的文件
- vb.net - 如何在 Visual Basic 中计算所述月份的第二个星期二
- javascript - 图表中单个堆叠条的工具提示?
- oracle - 我有要求,其中列值需要分成两列 rangefrom 和 rangeto
- design-patterns - 在这种情况下,工厂方法模式如何使代码可维护
- php - 如何根据选择框值填充 Twitter Typeahead?
- reactjs - 处理子组件中的数据加载
- c# - 第一行作为 C# 中的列名导出到 Excel 文件
- javascript - 如何调用 catch() 方法“尝试捕获”promise 的异常?
- android - 为Android编译boringssl问题