Многопоточность и остановка потока - PullRequest
0 голосов
/ 09 апреля 2020

Мне действительно нужна помощь по моему проекту. Задача: Целью теста является создание вычисления π (Pi) с использованием различных вычислительных процессов, ускоренных многопоточностью. Используйте класс BigDecimal для лучшей точности. Используйте свои собственные классы исключений и упакуйте все классы в одну аккуратную концепцию пакета.

Я пытался реализовать метод Leibniz, и моя главная проблема заключалась в том, что я не знаю, как остановить поток из моего основного метода, в то время как потоки бегут. Мой Учитель показал нам пример своего метода Миана, и вы можете ясно видеть, что он запускает метод, например, с 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 = 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");

Вот мои первые идеи для реализации задачи (не запутайтесь, я в основном сосредоточен на методах "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;
}

И мой класс Thread выглядит так:

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;
}

} Я пытался реализовать «флаг», чтобы остановить его, но я не знать, как получить влияние на потоки, которые инициализируются в методе «startCalculation (numThreads)» из метода «stopCalculation ()».

Если у кого-то есть идея, пожалуйста, дайте мне знать. Хорошего дня и будьте здоровы :)

1 Ответ

1 голос
/ 10 апреля 2020

к предисловию; Я не создавал и не запускал ваш код, и я действительно не искал формулу Лейбница, поэтому я просто оставлю этот ответ на ваш вопрос о потоке.

Похоже, что вы столкнулись здесь с двумя проблемами:

  1. Вызов w.join() заставит ваше выполнение ждать, пока поток не завершится. К сожалению, поток никогда не завершится sh, потому что вы никогда не выйдете из startCalculation(). Это называется взаимоблокировкой, и это вызвано тем, что один поток вечно ожидает окончания до другого sh.
  2. Даже если ваше выполнение достигло этой точки, вы не знаете, как заставить потоки остановиться.

Для первой проблемы мой совет - использовать один из других полезных классов потоков Java. В этом случае вы должны изменить LeibnizThread для реализации Runnable вместо расширения Thread. Это все равно приведет к созданию нового потока, но вам больше не нужно беспокоиться о специфике.

Для второго вопроса вы можете просто переместить массив потоков из метода, чтобы он получал ограничен на уровне класса. Затем в stopCalculation() вы можете l oop через потоки и сказать им остановиться.

Я написал базовый c фреймворк для того, чтобы вы могли использовать Runnables в подобных случаях. Обратите внимание, что это только один из способов сделать это, и в библиотеке параллелизма 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;
        }
    }
}

И вот небольшой код, который его реализует. Это немного похоже на код вашего профессора.

    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));
    }

Результаты:

Current Result: 1586477696333
Current Result: 1586477697785
Current Result: 1586477698815
Current Result: 1586477699783
Current Result: 1586477700859
Workers actually stopped: true

Я много пропустил, потому что я не хочу делать вашу домашнюю работу для вас, но это должно помочь вам начать. Наслаждайтесь! :)

...