代码 Parallel.For 仅适用于调试模式
Code Parallel.For works only in Debug mode
此代码仅在调试模式下才能正常工作。我不明白哪里出了问题。
我尝试修改代码,但没有任何进展。如果第一个参数为真,我想将调用函数 newRow 的结果添加到 hashSet。
foreach (structNumbers sn in numbers)
//Parallel.ForEach(numbers, new ParallelOptions { MaxDegreeOfParallelism = 1 }, (sn) =>
{
#region
//for (Int32 v = 0; v < 16; v++)
Parallel.For<Tuple<Boolean, mpz_t, mpz_t>>(0, 16,
()=> { return new Tuple<Boolean, mpz_t, mpz_t>(false, 0, 0); },
(v, pls, state) =>
{
#region
Interlocked.Increment(ref countChecked);
//if (newRow(i, j, v, t, index, sn.n, sn.m, out nMin, out mMin) == true)
//lock(thisLock)
Tuple<Boolean, mpz_t, mpz_t> res = newRow(i, j, v / 4, v % 4, index, sn.n, sn.m);
state = new Tuple<bool, mpz_t, mpz_t>(res.Item1, res.Item2, res.Item3);
return state;
#endregion
},
state => {
lock (thisLock)
{
if (state.Item1 == true)
{
#region
numbersTemp.Add(new structNumbers(state.Item2, state.Item3));
//numbersTemp.Add(new structNumbers(nMin, mMin));
//Console.WriteLine("bla");
#endregion
}
}
}
);
#endregion
}
//);
如果你想聚合并行循环的多个结果,那么你可以这样做:
object lockObject=new object();
HashSet<TResult> result=new HashSet<TResult>();
Parallel.For(0,16,() => new List<TResult>(),(i,pls,list) => {
TResult r;
if(TryGetResult(i,out r)) {
list.Add(r);
}
return list;
},list => {
lock(lockObject) {
result.UnionWith(list);
}
});
但是如果 TryGetResult
代表大工作量(至少它应该大到足以证明并行循环的开销是合理的),你可以这样做:
object lockObject=new object();
HashSet<TResult> result=new HashSet<TResult>();
Parallel.For(0,16,i => {
TResult r;
if(TryGetResult(i,out r)) {
lock(lockObject) {
result.Add(r);
}
}
});
或者这样:
ConcurrentBag<TResult> bag=new ConcurrentBag<TResult>();
Parallel.For(0,16,i => {
TResult r;
if(TryGetResult(i,out r)) {
bag.Add(r);
}
});
HashSet<TResult> result=new HashSet<TResult>(bag);
在我看来,当你有标量结果时,使用 TLocal
类型参数重载 Parallel.For
更合适,没有锁就无法更新:
object lockObject=new object();
BigInteger result=BigInteger.Zero;
Parallel.For(0,16,i => {
BigInteger r=GetBigInteger(i);
lock(lockObject) {
result+=r;
}
});
在这段代码中,添加两个 BigInteger
可能是昂贵的操作,并且不能并行完成,所以你最好像这样重写代码:
object lockObject=new object();
BigInteger result=BigInteger.Zero;
Parallel.For(0,16,() => BigInteger.Zero,(i,pls,subResult) => {
return subResult+GetBigInteger(i);
},subResult => {
lock(lockObject) {
result+=subResult;
}
});
此版本 Parallel.For()
的工作方式是,它首先将输入拆分为多个批次。然后对于每个批次,使用 localInit
委托初始化本地状态,为批次中的每个数字调用 body
委托,将最后一个状态传递给它并期望它到 return 一个新的状态,最后,在最终状态上调用 localFinally
。
由于您忽略了 body
输入的状态,因此您仅将每批中的最后一项添加到结果中。
PetSerAl 的回答显示了可能的解决方案。最合理的开始可能是最简单的 lock
。如果结果太慢,请寻找更复杂的解决方案,包括那些使用本地状态的解决方案。
此代码仅在调试模式下才能正常工作。我不明白哪里出了问题。
我尝试修改代码,但没有任何进展。如果第一个参数为真,我想将调用函数 newRow 的结果添加到 hashSet。
foreach (structNumbers sn in numbers)
//Parallel.ForEach(numbers, new ParallelOptions { MaxDegreeOfParallelism = 1 }, (sn) =>
{
#region
//for (Int32 v = 0; v < 16; v++)
Parallel.For<Tuple<Boolean, mpz_t, mpz_t>>(0, 16,
()=> { return new Tuple<Boolean, mpz_t, mpz_t>(false, 0, 0); },
(v, pls, state) =>
{
#region
Interlocked.Increment(ref countChecked);
//if (newRow(i, j, v, t, index, sn.n, sn.m, out nMin, out mMin) == true)
//lock(thisLock)
Tuple<Boolean, mpz_t, mpz_t> res = newRow(i, j, v / 4, v % 4, index, sn.n, sn.m);
state = new Tuple<bool, mpz_t, mpz_t>(res.Item1, res.Item2, res.Item3);
return state;
#endregion
},
state => {
lock (thisLock)
{
if (state.Item1 == true)
{
#region
numbersTemp.Add(new structNumbers(state.Item2, state.Item3));
//numbersTemp.Add(new structNumbers(nMin, mMin));
//Console.WriteLine("bla");
#endregion
}
}
}
);
#endregion
}
//);
如果你想聚合并行循环的多个结果,那么你可以这样做:
object lockObject=new object();
HashSet<TResult> result=new HashSet<TResult>();
Parallel.For(0,16,() => new List<TResult>(),(i,pls,list) => {
TResult r;
if(TryGetResult(i,out r)) {
list.Add(r);
}
return list;
},list => {
lock(lockObject) {
result.UnionWith(list);
}
});
但是如果 TryGetResult
代表大工作量(至少它应该大到足以证明并行循环的开销是合理的),你可以这样做:
object lockObject=new object();
HashSet<TResult> result=new HashSet<TResult>();
Parallel.For(0,16,i => {
TResult r;
if(TryGetResult(i,out r)) {
lock(lockObject) {
result.Add(r);
}
}
});
或者这样:
ConcurrentBag<TResult> bag=new ConcurrentBag<TResult>();
Parallel.For(0,16,i => {
TResult r;
if(TryGetResult(i,out r)) {
bag.Add(r);
}
});
HashSet<TResult> result=new HashSet<TResult>(bag);
在我看来,当你有标量结果时,使用 TLocal
类型参数重载 Parallel.For
更合适,没有锁就无法更新:
object lockObject=new object();
BigInteger result=BigInteger.Zero;
Parallel.For(0,16,i => {
BigInteger r=GetBigInteger(i);
lock(lockObject) {
result+=r;
}
});
在这段代码中,添加两个 BigInteger
可能是昂贵的操作,并且不能并行完成,所以你最好像这样重写代码:
object lockObject=new object();
BigInteger result=BigInteger.Zero;
Parallel.For(0,16,() => BigInteger.Zero,(i,pls,subResult) => {
return subResult+GetBigInteger(i);
},subResult => {
lock(lockObject) {
result+=subResult;
}
});
此版本 Parallel.For()
的工作方式是,它首先将输入拆分为多个批次。然后对于每个批次,使用 localInit
委托初始化本地状态,为批次中的每个数字调用 body
委托,将最后一个状态传递给它并期望它到 return 一个新的状态,最后,在最终状态上调用 localFinally
。
由于您忽略了 body
输入的状态,因此您仅将每批中的最后一项添加到结果中。
PetSerAl 的回答显示了可能的解决方案。最合理的开始可能是最简单的 lock
。如果结果太慢,请寻找更复杂的解决方案,包括那些使用本地状态的解决方案。