英文:
Multi-threading and stopping a thread
问题
我真的需要一些关于我的项目的帮助。
任务:
测试的目标是使用多线程加速的各种计算过程创建一个π(圆周率)计算。
为了更好的精度,使用BigDecimal类。
使用自己的异常类,并将所有类打包在一个整洁的包概念中。
我尝试实现莱布尼茨方法,我遇到的主要问题是我不知道如何在我的主方法中停止线程,而线程正在运行。我的老师向我们展示了他的主方法的一个示例,你可以清楚地看到他是如何以例如4个线程启动方法的。几秒钟后,他能够停止所有线程。
这是他主类的示例:
CalculatePi pi = new Leibniz();
System.out.println("Start: " + pi.getMethodName());
pi.startCalculation(4); //four threads
int prec = 0;
BigDecimal result = BigDecimal.ZERO;
long timeStart = System.currentTimeMillis();
while(prec < MAX_PRECISION) {
someDelay(); //give some time to calculate
BigDecimal newResult = pi.getValue();
int newPrec = precision(result, newResult);
if(newPrec != prec) {
System.out.println("pi (" + newPrec + "): " + newResult);
prec = newPrec;
}
result = newResult;
}
long timeStop = System.currentTimeMillis();
pi.stopCalculation();
System.out.println( (timeStop - timeStart) + " ms");
System.out.println(pi.getInternalSteps() + " calculation steps");
这是我首次实现任务的想法(不要被搞混,我主要关注由接口提供的"startCalculation(int numThreads)"和"stopCalculation()"方法):
// Methode soll Leibniz Verfahren mit mehreren Threads berechnen
@Override
public boolean startCalculation(int numThreads) {
// Threads müssen in Array gespeichert werden um damit zu arbeiten
LeibnizThread[] threadSpeicher = new LeibnizThread[numThreads];
for(int i = 0; i < numThreads; i++) {
// Neuen Thread initialisieren und im Array speichern
threadSpeicher[i] = new LeibnizThread(numThreads, i);
//Thread starten
threadSpeicher[i].start();
}
//Warten bis alle Threads fertig sind und ihr ergebnis berechnet haben
for(LeibnizThread w : threadSpeicher)
try {
w.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BigDecimal sum = new BigDecimal(0.0);
//Summe aller Threads zusammenrechnen
for(LeibnizThread w : threadSpeicher) {
System.out.println(w.getResult() + " Zwischenergebnis");
sum = sum.add(w.getResult());
}
//Summe wird mit 4 multipliziert, um finales Ergebnis zu erhalten
this.value = sum.multiply(new BigDecimal(4));
System.out.println("Ergebnis: " + this.value);
return true;
}
//Methode soll Threads aus Methode startCalculation(numThreads) stoppen, aber wie ?
@Override
public void stopCalculation() {
flag = true;
}
我的线程类看起来是这样的:
public class LeibnizThread extends Thread {
private int threadRemainder;
private int numThreads;
private BigDecimal result = new BigDecimal(0.0);
private volatile boolean flag = false;
public LeibnizThread(int threadCount, int threadRemainder) {
this.numThreads = threadCount;
this.threadRemainder = threadRemainder;
}
public void run() {
BigDecimal counter = new BigDecimal("1");
while( !flag ) {
if(counter.intValue() % numThreads == threadRemainder)
if(counter.remainder(new BigDecimal("2")).equals(BigDecimal.ONE)) {
result = result.add(BigDecimal.ONE.divide(((new BigDecimal("2").multiply(counter).subtract(BigDecimal.ONE))), 100, RoundingMode.HALF_UP));
}else {
result = result.subtract(BigDecimal.ONE.divide(((new BigDecimal("2").multiply(counter).subtract(BigDecimal.ONE))), 100, RoundingMode.HALF_UP));
}
counter = counter.add(new BigDecimal("1"));
}
}
public BigDecimal getResult() {
return this.result;
}
public void setFlagTrue() {
flag = true;
}
}
我尝试实现了一个“flag”来停止线程,但我不知道如何在方法“startCalculation(numThreads)”中初始化的线程上产生影响,该方法来自于方法“stopCalculation()”。
如果有人有想法,请告诉我。祝你有美好的一天,保持健康 :)
英文:
I could really need some Help on my project.
Task :
The aim of the test is to create a π (Pi) calculation using various
Computation processes accelerated by multi-threading.
Use the BigDecimal class for better precision.
Use your own exception classes and pack all classes in one
neat package concept.
I tried to implement the Leibniz-method and my main issue was that i dont know how to stop a Thread from my main method while the Threads are running. My Teacher showed us and example of his mian method and you can clearly see that he is starting the method with for example 4 threads. And a few seconds later he is able to stop all of the threads.
Here is his example of the main class:
CalculatePi pi = new Leibniz();
System.out.println("Start: " + pi.getMethodName());
pi.startCalculation(4); //four threads
int prec = 0;
BigDecimal result = BigDecimal.ZERO;
long timeStart = System.currentTimeMillis();
while(prec < MAX_PRECISION) {
someDelay(); //give some time to calculate
BigDecimal newResult = pi.getValue();
int newPrec = precicion(result, newResult);
if(newPrec != prec) {
System.out.println("pi (" + newPrec + "): " + newResult);
prec = newPrec;
}
result = newResult;
}
long timeStop = System.currentTimeMillis();
pi.stopCalculation();
System.out.println( (timeStop - timeStart) + " ms");
System.out.println(pi.getInternalSteps() + " calulation steps");
Here are my first Ideas to Implement the task (dont get confused i mainly focus on the method "startCalculation(int numThreads)" and "stopCalculation()" which are both given by an interface)
// Methode soll Leibniz Verfahren mit mehreren Threads berechnen
@Override
public boolean startCalculation(int numThreads) {
// Threads müssen in Array gespeichert werden um damit zu arbeiten
LeibnizThread[] threadSpeicher = new LeibnizThread[numThreads];
for(int i = 0; i < numThreads; i++) {
// Neuen Thread initialisieren und im Array speichern
threadSpeicher[i] = new LeibnizThread(numThreads, i);
//Thread starten
threadSpeicher[i].start();
}
//Warten bis alle Threads fertig sind und ihr ergebnis berechnet haben
for(LeibnizThread w : threadSpeicher)
try {
w.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BigDecimal sum = new BigDecimal(0.0);
//Summe aller Threads zusammenrechnen
for(LeibnizThread w : threadSpeicher) {
System.out.println(w.getResult() + " Zwischenergebnis");
sum = sum.add(w.getResult());
}
//Summe wird mit 4 multipliziert, um finales Ergebnis zu erhalten
this.value = sum.multiply(new BigDecimal(4));
System.out.println("Ergebnis: " + this.value);
return true;
}
//Methode soll Threads aus Methode startCalculation(numThreads) stoppen, aber wie ?
@Override
public void stopCalculation() {
flag = true;
}
And my Thread class looks like that:
public class LeibnizThread extends Thread {
private int threadRemainder;
private int numThreads;
private BigDecimal result = new BigDecimal(0.0);
private volatile boolean flag = false;
public LeibnizThread(int threadCount, int threadRemainder) {
this.numThreads = threadCount;
this.threadRemainder = threadRemainder;
}
public void run() {
BigDecimal counter = new BigDecimal("1");
while( !flag ) {
if(counter.intValue() % numThreads == threadRemainder)
if(counter.remainder(new BigDecimal("2")).equals(BigDecimal.ONE)) {
result = result.add(BigDecimal.ONE.divide(((new BigDecimal("2").multiply(counter).subtract(BigDecimal.ONE))), 100, RoundingMode.HALF_UP));
}else {
result = result.subtract(BigDecimal.ONE.divide(((new BigDecimal("2").multiply(counter).subtract(BigDecimal.ONE))), 100, RoundingMode.HALF_UP));
}
counter = counter.add(new BigDecimal("1"));
}
}
public BigDecimal getResult() {
return this.result;
}
public void setFlagTrue() {
flag = true;
}
}
I tried to implement a "flag" to make it stop but i dont know how to get impact on the threads which are initialized in the method "startCalculation(numThreads)" from the method "stopCalculation()" .
If anyone has an idea please let me know. Have a nice day and stay healthy
答案1
得分: 1
package com.sandbox;
import java.math.BigDecimal;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Leibniz implements CalculatePi {
private Worker[] workers;
@Override
public boolean startCalculation(int numThreads) {
ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
workers = new Worker[numThreads];
for (int i = 0; i < numThreads; i++) {
Worker worker = new Worker();
workers[i] = worker;
executorService.submit(worker);
}
return true;
}
@Override
public void stopCalculation() {
for (Worker worker : workers) {
worker.stopExecution();
}
}
@Override
public BigDecimal getValue() {
BigDecimal result = BigDecimal.ZERO;
for (Worker worker : workers) {
result = result.max(worker.getCurrentResult());
}
return result;
}
private class Worker implements Runnable {
private volatile boolean stopExecution = false;
private BigDecimal currentResult;
Worker() {
// Pass in whatever you need to do the work
}
@Override
public void run() {
while (!stopExecution) {
currentResult = new BigDecimal(System.currentTimeMillis());
}
}
void stopExecution() {
this.stopExecution = true;
}
BigDecimal getCurrentResult() {
return currentResult;
}
}
}
public static void main(String[] args) throws InterruptedException {
CalculatePi pi = new Leibniz();
pi.startCalculation(4);
for (int i = 0; i < 5; i++) {
sleep(1000);
System.out.println("Current Result: " + pi.getValue());
}
pi.stopCalculation();
BigDecimal finalResult = pi.getValue();
sleep(1000);
BigDecimal verifyFinalResult = pi.getValue();
System.out.println("Workers actually stopped: " + finalResult.equals(verifyFinalResult));
}
Results:
Current Result: 1586477696333
Current Result: 1586477697785
Current Result: 1586477698815
Current Result: 1586477699783
Current Result: 1586477700859
Workers actually stopped: true
<details>
<summary>英文:</summary>
To preface; I haven't built and run your code and I haven't really looked up the Leibniz formula, so I'll just keep this answer to your threading question.
It looks like you are facing two problems here:
1. Calling `w.join()` will cause your execution to wait until the thread finishes. Unfortunately, the thread will never finish because you will never exit `startCalculation()`. This is called a deadlock and it's caused when one thread is forever waiting for another to finish.
2. Even if your execution got to that point, you don't know how to tell the threads to stop.
For the first issue, my advice would be to use one of Java's other helpful thread classes. In this case, you should change LeibnizThread to implement `Runnable` instead of extending `Thread`. This will still cause a new thread to be created, but you mostly don't need to worry about the specifics.
For the second issue, you can just move the array of threads out of the method so it gets scoped at the class level. Then, in `stopCalculation()` you can loop through the threads and tell them to stop.
I wrote up a basic framework for how you can use Runnables in cases like this. Note that this is only one way to do it and there are tons of helpful classes in Java's concurrency library. So look around and see all the tools that are available!
```java
package com.sandbox;
import java.math.BigDecimal;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Leibniz implements CalculatePi {
private Worker[] workers;
@Override
public boolean startCalculation(int numThreads) {
// The executor service handles your thread execution for you
ExecutorService executorService = Executors.newFixedThreadPool(numThreads);
// Start you threads and save a reference to them so you can call them later
workers = new Worker[numThreads];
for (int i = 0; i < numThreads; i++) {
Worker worker = new Worker();
workers[i] = worker;
executorService.submit(worker); // This starts the thread. It calls worker.run().
}
return true;
}
@Override
public void stopCalculation() {
for (Worker worker : workers) {
worker.stopExecution();
}
}
@Override
public BigDecimal getValue() {
BigDecimal result = BigDecimal.ZERO;
for (Worker worker : workers) {
// Do whatever thread consolidation work you need to do here to get a single result
result = result.max(worker.getCurrentResult());
}
return result;
}
private class Worker implements Runnable {
private volatile boolean stopExecution = false; // "volatile" helps make sure the thread actually stops when you want it to by avoiding CPU caches
private BigDecimal currentResult;
Worker() {
// Pass in whatever you need to do the work
}
@Override
public void run() {
while (!stopExecution) {
// Do all of your multi-threaded computation here, setting the currentResult as you go
currentResult = new BigDecimal(System.currentTimeMillis()); // Example.
}
}
void stopExecution() {
this.stopExecution = true;
}
BigDecimal getCurrentResult() {
return currentResult;
}
}
}
And here's a little code that exercises it. It looks a bit like your professor's code.
public static void main(String[] args) throws InterruptedException {
CalculatePi pi = new Leibniz();
pi.startCalculation(4);
for (int i = 0; i < 5; i++) {
sleep(1000);
System.out.println("Current Result: " + pi.getValue());
}
pi.stopCalculation();
BigDecimal finalResult = pi.getValue();
sleep(1000);
BigDecimal verifyFinalResult = pi.getValue();
System.out.println("Workers actually stopped: " + finalResult.equals(verifyFinalResult));
}
Results:
Current Result: 1586477696333
Current Result: 1586477697785
Current Result: 1586477698815
Current Result: 1586477699783
Current Result: 1586477700859
Workers actually stopped: true
I left a lot out because I don't want to do your homework for you, but this should help you get started. Enjoy!
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论