我在大型计算中使用并行流,但是当我执行它时结果很奇怪。在纯[SEQ]
- 进程调度中执行所有操作时,它只能正常工作。
为了找到问题,我使用了一个简单的计算(工作是将“迭代”中的所有自然数加到零):
private long sum = 0; // global helper
public static void main(String args[])
{
int iterations = 50000; // the target number
// solving the task parallel
// ---------------------------------- <Section-under-Test>.START
final long startPAR = System.nanoTime();
IntStream.range(1, iterations+1).parallel().forEach(i->{
sum += i;
});
final long durationPAR = ( ( System.nanoTime() - startPAR ) / 1_000_000 );
// ---------------------------------- <Section-under-Test>.FINISH
long sumParallel = sum; sum = 0; // save + reset variable
System.out.println( "Sum parallel: " + sumParallel
+ " TOOK: " + durationPAR
);
// solving the task linear using one core
// ---------------------------------- <Section-under-Test>.START
final long startSEQ = System.nanoTime();
for(int i = 1; i < iterations+1; i++)
{
sum += i;
}
final long durationSEQ = ( ( System.nanoTime() - startSEQ ) / 1_000_000 );
// ---------------------------------- <Section-under-Test>.FINISH
System.out.println( "Sum serial: " + sum
+ " TOOK: " + durationSEQ
);
System.out.println((sum == sumParallel));
}
奇怪的是,每次执行并行部分时都会得到不同的输出:
Sum parallel: 354519954
Sum serial: 1250025000
false
---------------------------
Sum parallel: 453345292
Sum serial: 1250025000
false
---------------------------
Sum parallel: 613823840
Sum serial: 1250025000
false
我是否忽略了这一点,并希望在错误的地方使用并行计算?
在我更大的计算中,我正在计算要添加的并行值,在本例中为总和。所以这很好用。但是,如何将此结果正确添加到全局变量?
答案 0 :(得分:2)
因为sum += i;
不是原子的,所以它涉及多个操作:
i
sum
sum
加i
sum
在执行这些操作期间的任何时候,我们都有其他线程执行相同的操作。如果两个线程同时读取sum
(操作2),则可以保证得到不同的结果,因为当它们都计算sum + i
时,两个线程都不会考虑另一个线程。
如果您想并行执行此类计算,请改用AtomicLong
。 addAndGet
之类的操作保证是原子的,也就是说,它保证在一个步骤中发生,而不是分解为上述步骤。
答案 1 :(得分:2)
只需使用 .sum()
方法,以便没有线程在共享状态下运行,因此无需进行同步或原子访问:
int sum = IntStream.rangeClosed(1, iterations).parallel().sum();
如果您需要更多控制计算,可以使用 .reduce()
方法:
int sum = IntStream.rangeClosed(1, iterations).parallel().reduce(0, (a, b) -> a + b);