我正在尝试使用Gregory-Leibniz
算法来计算PI,但是我总是仅在使用多线程时才得到错误的值。单线程工作正常。
我认为问题是共享的k
值导致计算混乱。
错误的PI值:
选择一个选项:4多少点? 100000多少个线程? 32 并发的Gregory-Leibniz估计PI值:2.7663972054374577。 在121.578657毫秒内执行。
请帮忙吗?
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* The Class GregoryLeibniz.
*/
public class GregoryLeibniz {
/** The in circle. */
private double factor;
/** The sum. */
private double sum;
/** The points. */
private long points;
/** The n processors. */
private int nProcessors;
private long k;
/**
* Instantiates a new gregory leibniz.
*
* @param points the points
* @param nProcessors the n processors
*/
public GregoryLeibniz(long points, int nProcessors) {
super();
this.points = points;
this.nProcessors = nProcessors;
}
/**
* The Class GregoryLeibnizImpl.
*/
public class GregoryLeibnizImpl implements Runnable {
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if(k % 2 == 0) factor = 1.0;
else factor = -1.0;
sum += factor / (2*k +1);
}
}
/**
* Calculate PI.
*
* @return the double
*/
public double calculatePI() {
ExecutorService executor = Executors.newWorkStealingPool(nProcessors);
for (k = 0; k < points; k++) {
Runnable worker = new GregoryLeibnizImpl();
executor.execute(worker);
}
executor.shutdown();
while(!executor.isTerminated()) { }
return 4.0 * sum;
}
}
答案 0 :(得分:2)
GregoryLeibnizImpl的每个实例都需要独立运行。或者您需要互斥锁。或两者兼有。
GregoryLeibnizImpl需要将“ k”作为构造函数参数并将其存储为成员变量。
您需要在sum
周围使用互斥/防护。否则,您需要在calculatePI
函数末尾“总结”工作线程对象的所有结果。
此行:
while(!executor.isTerminated()) { }
将消耗整个内核并杀死代码性能。请改用awaitTermination方法。
更新
我想要一些练习,所以我重组了您的代码以寻求一个有价值的解决方案。也许我可以帮忙...
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* The Class GregoryLeibniz.
*/
public class GregoryLeibniz {
/** The n processors. */
private int nProcessors;
private long points;
private long itemsPerThread;
private long itemsInFirstThread;
/**
* Instantiates a new gregory leibniz.
*
* @param points the points
* @param nProcessors the n processors
*/
public GregoryLeibniz(long points, int nProcessors) {
this.points = points;
this.nProcessors = nProcessors;
this.itemsPerThread = this.points / this.nProcessors;
this.itemsInFirstThread += this.itemsPerThread + this.points - this.itemsPerThread * this.nProcessors;
}
/**
* The Class GregoryLeibnizImpl.
*/
public class GregoryLeibnizImpl implements Runnable {
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
long start;
long end;
public double result;
public GregoryLeibnizImpl(long start, long end)
{
this.start = start;
this.end = end;
}
@Override
public void run() {
int factor = ((start % 2)!=0) ? -1 : 1;
for (long i = start; i <= end; i++) {
result += factor / (double)(i*2+1);
factor *= -1;
}
}
}
/**
* Calculate PI.
*
* @return the double
*/
public double calculatePI() {
ExecutorService executor = Executors.newWorkStealingPool(nProcessors);
long start = 1;
long end = itemsInFirstThread;
GregoryLeibnizImpl [] workers = new GregoryLeibnizImpl[this.nProcessors];
for (int t = 0; t < this.nProcessors; t++) {
GregoryLeibnizImpl worker = new GregoryLeibnizImpl(start, end);
workers[t] = worker;
executor.execute(worker);
start += this.itemsPerThread;
end += this.itemsPerThread;
}
executor.shutdown();
while (executor.isTerminated() == false) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
}
double result = 0;
for (int t = 0; t < this.nProcessors; t++) {
result += workers[t].result;
}
result += 1;
result *= 4;
return result;
}
public static void main(String [] args) {
var gl = new GregoryLeibniz(1000000, 4);
double d = gl.calculatePI();
System.out.println(d);
}
}
答案 1 :(得分:0)
尝试同步运行方法。如果让k不使用互斥锁,则一个线程将检查k的模数,设置系数,然后它将进入睡眠状态,而下一个线程将检查模数的设置(相同)因数,求和+ =填充并进入睡眠状态。在那之后,第一个线程也将用旧因子对sum ==进行求和。
答案 2 :(得分:0)
您需要给您的工作人员k
的副本。除此之外,让您的工人只迈出第一步,您并不能真正赢得很多。创建的工作人员不应超过可用的CPU(核)数量,并为每个工作人员分配一部分用于计算。
还请注意,这仍然给您带来错误的sum
,因为如果两个工作人员同时对其进行读写,那么其中一个结果将会丢失。
让每个工作人员维护一个私有sum
,然后在完成所有工作后,将其私有总和添加到主线程中。
类似这样的东西:
public class GregoryLeibnizImpl implements Runnable {
private int start;
private int end;
private int sum;
public GregoryLeibnizImpl(int start, int end) {
this.start = start;
this.end = end;
}
@Override
public void run() {
// loop from start to end using your sum algorithm
}
}
然后将循环更改为以下内容:
for (int k = 0; k < points; k += points / numthreads) {
Runnable worker = new GregoryLeibnizImpl(k, k + points / numthreads);
executor.execute(worker);
}
您需要将这些工作实例保存在某个地方,例如一个列表,以便一旦完成就可以收集其结果。