Współbieżność oraz zadanie w tle

Odpowiedz Nowy wątek
2017-12-04 11:00
0

Witam.

Dopiero zaczynam moją przygotę z programowaniem współbieżnym i zostało mi postawione na uczelni zadanie (treść na samym końcu). Zadanie napisałem, jednak problem polega na tym, że po sprawdzeniu go przez program sprawdzający otrzymałem jedynie część punktów, otrzymując takie powiadomienia wyjaśniające powód odjęcia punktów:
- do kończenia watku należało uzyć metody interrupt()
-należało sprawdzać flagę interrupted metodą interrupted lub isInterrupted
- brak wątku przerywającego w metodzie main
Oczywiście nie jestem do końca pewny czy zadanie wykonałem poprawnie...(jak wiadomo w przypadku wątków trochę zależy też od szczęścia przy testowaniu)...

public class StringTask implements Runnable {

    private TaskState currentState;
    private String stringToMultiply;
    private int howMany;
    private String resultString;
    private Thread task;
    private volatile boolean taskShouldBeRunning;

    public StringTask(String stringToMultiply,int howMany) {
        this.howMany=howMany;
        this.stringToMultiply=stringToMultiply;
        this.currentState=TaskState.CREATED;
        this.task=new Thread(this);
        this.taskShouldBeRunning=false;
        this.resultString="";
    }

    //2 konstruktor po to by mozna bylo uruchomic nastepne zadanie (watek)
    //zaczynajac od stanu "wypracowanego" przez inny
    public StringTask(String stringToMultiply,int howMany,String resultString ) {
        this.howMany=howMany;
        this.stringToMultiply=stringToMultiply;
        this.currentState=TaskState.CREATED;
        this.task=new Thread(this);
        this.taskShouldBeRunning=false;
        this.resultString=resultString;
    }
    //2 get'ery po to by mozna bylo uruchamiac zadania
    //poczynajac od stanu "wypracowanego" przez inne zadanie
    public String getStringToMultiply() {
        return stringToMultiply;
    }

    public int getHowMany() {
        return howMany;
    }

    /************WYMAGANE METODY*******************/

    public void start() {

        taskShouldBeRunning=true;
        task.start();
    }

    public void  abort() {
        taskShouldBeRunning=false;
        //currentState=TaskState.ABORTED; po to by uzyskac 2 efekt.. z tresci zadania...
        //task.interrupt();                                                                                                //chyba tutaj (zgodnie z błędami ze sprawdzania musi być interrupt?)
    }
    public String getResult() {
        return resultString;

    }

    //zwraca biezacy stan zadania
    public TaskState getState() {
        return currentState;
    }
    public boolean isDone() {
        if(currentState.equals(TaskState.READY)||currentState.equals(TaskState.ABORTED))return true;
            return false;
    }

    //w metodzie run() wykonywane jest powielanie napisu
    @Override
    public void run() {
        multiplyString();
    }

     private void multiplyString() {
        while(taskShouldBeRunning) {                    //czy tutaj trzeba sprawdzać flagę interrupt?? 
            int helper=howMany;
            for(int i=0;i<helper && taskShouldBeRunning;i++) {
                currentState=TaskState.RUNNING;
                resultString=resultString+stringToMultiply;
                howMany--;
            }
            if(howMany==0) {
                currentState=TaskState.READY;
                return ;
            }
        }
        currentState=TaskState.ABORTED;
    }

}

Poniżej jeszcze main i w miarę oczywisty enum...

public enum TaskState {
    CREATED,RUNNING,ABORTED,READY;

}

public class Main {
  public static void main(String[] args) throws InterruptedException {
    StringTask task = new StringTask("A", 70000);
    System.out.println("Task " + task.getState());
    task.start();
    if (args.length > 0 && args[0].equals("abort")) { 
    /*<- tu zapisać kod  przerywający działanie tasku po sekundzie 
         i uruchomić go w odrębnym wątku
    */
        Thread.sleep(1000);
        task.abort();
        StringTask task2=new StringTask(task.getStringToMultiply(),task.getHowMany(),task.getResult());

    }
    while (!task.isDone()) {
      Thread.sleep(500);
      switch(task.getState()) {
        case RUNNING: System.out.print("R."); break;
        case ABORTED: System.out.println(" ... aborted."); break;
        case READY: System.out.println(" ... ready."); break;
        default: System.out.println("unknown state");
      }

    }
    System.out.println("Task " + task.getState());
    System.out.println(task.getResult().length());
  }
}

I treść zadania...

Uruchamianie i zatrzymywanie równoległego działania kodów

Zbudować klasę StringTask, symulująca długotrwałe obliczenia, tu polegające na konkatenacji napisow.
Konstruktor klasy otrzymuje jako argument napis do powielenia oraz liczbę oznaczającą ile razy ten napis ma być powielony.
Klasa winna implementować interfejs Runnable, a w jej metodzie run() wykonywane jest powielenia napisu, przy czym to powielenia ma się odbywac za pomoca operatora '+' stosowanego wobec zmiennych typu String (to właśnie długotrwała operacja). Użycie '+' jest warunkiem obowiązkowe.

Obiekt klasy StringTask traktujemy jako zadanie, które może się wykonywać równolegle z innymi.
Możliwe stany zadania to:

CREATED  - zadanie utworzone, ale nie zaczęło się jeszcze wykonywać,
RUNNING - zadanie się wykonuje w odrebnym wątku
ABORTED - wykonanie zadania zostało przerwane
READY - zadanie zakończyło się pomyślnie i sa gotowe wyniki.

W klasie StringTask zdefiniować metody:

  public String getResult()  - zwracającą wynik konkatenacji
  public TaskState getState()  - zwracającą stan zadania
  public void start() - uruchamiającą zadanie w odrębnym watku
  public void abort() - przerywającą wykonanie kodzu zadania i działanie watku
  public boolean isDone()  - zwracająca true, jeśli wykonanie zadania się zakończyło normalnie lub przez przerwanie, false w przeciwnym razie

Poniższy kod program:

public class Main {

  public static void main(String[] args) throws InterruptedException {
    StringTask task = new StringTask("A", 70000);
    System.out.println("Task " + task.getState());
    task.start();
    if (args.length > 0 && args[0].equals("abort")) { 
    /*<- tu zapisać kod  przerywający działanie tasku po sekundzie 
         i uruchomic go w odrębnym wątku
    */
    }
    while (!task.isDone()) {
      Thread.sleep(500);
      switch(task.getState()) {
        case RUNNING: System.out.print("R."); break;
        case ABORTED: System.out.println(" ... aborted."); break;
        case READY: System.out.println(" ... ready."); break;
        default: System.out.println("uknown state");
      }

    }
    System.out.println("Task " + task.getState());
    System.out.println(task.getResult().length());
  }

}

uruchominy bez argumentu powinien wyprowadzić coś w rodzaju:
Task CREATED
R.R.R.R.R.R.R.R.R. ... ready.
Task READY
70000

a uruchomiony z argumentem "abort" może wyprowadzić:
Task CREATED
R. ... aborted.
Task ABORTED
31700

Uwaga 1. Plik Main.java może być modyfikowany tylko w miejscu oznaczonym /<- /
Uwaga 2. Nie wolno uzywac metody System.exit(...)

Z góry dziękuję wszyskitm, którzy przekopią się przez tak długą treść i postarają się mi pomóc.
Pozdrawiam.

edytowany 1x, ostatnio: ŁF, 2017-12-04 11:33

Pozostało 580 znaków

2017-12-04 17:47
private void multiplyString() {
        while(taskShouldBeRunning) {                    //czy tutaj trzeba sprawdzać flagę interrupt?? 
            int helper=howMany;
            for(int i=0;i<helper && taskShouldBeRunning;i++) {
                currentState=TaskState.RUNNING;
                resultString=resultString+stringToMultiply;
                howMany--;
            }
            if(howMany==0) {
                currentState=TaskState.READY;
                return ;
            }
        }
        currentState=TaskState.ABORTED;
    }

Tak, możesz nawet wstawić 2 sprawdzenia, jedno przed konkatenacją a drugie zaraz po, żeby jak najszybciej task został przerwany (nie wiesz w którym momencie wleci komenda). Interrupt tylko ustawia stan wątku na "przerwany", normalnie wątek kończy się jak dobiegnie końca.

EDIT: "tutaj" tzn w tej metodzie, nie w linijce z komentarzem : P

EDIT2: I to przerwanie z tego co zrozumiałem to ma być w oddzielnym wątku uruchomione, tzn że ma "czuwać" oddzielny wątek sprawdzający czy nie przerywasz zadania. Innymi słowy, zrób tak żeby operacja trwała np 20 sekund i spróbuj ją przerwać w trakcie. Aktualnie on przeleci przez if'a zaraz na starcie i zawiesi się na pętli while i już na tego if'a więcej nie spojrzy

I nie widzę nigdzie w treści zadania, że ma być możliwość wykonywania od stanu "wypracowanego".

W metodzie abort ustawiasz stan wątku na przerwany (i enuma na ABORTED żeby spełnić wymogi dotyczące metody isDone()) a w metodzie run, sprawdzasz czy jest przerwany, jeśli tak to return.

I te 7 tysięcy przypisań enum'a w pętli to tak dla pewnośći? : D

edytowany 5x, ostatnio: pedegie, 2017-12-04 19:12

Pozostało 580 znaków

2017-12-08 16:11
0

Dzięki za odpowiedź, niestety nie miałem (aż do dziś bo dzisiaj o północy ostateczny termin oddania #studenciak ) czasu ogarnąć jej ... Trochę zadziałałem... i ale czy to sie trzyma "kupy" to nie wiem...
Zmiany jakie na razie zrobiłem:

    @Override
    public void run() {
        multiplyString();
    }

     private void multiplyString() {
         while(!task.isInterrupted()&&howMany!=0) {

            int helper=howMany;
            currentState=TaskState.RUNNING;
            for(int i=0;i<helper && !(task.isInterrupted());i++) {
                resultString=resultString+stringToMultiply;
                howMany--;
            }

        }
         //"czynnosci porzadkowe"
        if(howMany==0) {
            currentState=TaskState.READY;
        }else {
            currentState=TaskState.ABORTED;
        }

        return ;//"wyrazny" return ...
    }

/***************************************/
//metoda abort, przyjela teraz nastepujaca postac: 
public void  abort() {
        //taskShouldBeRunning=false;
        currentState=TaskState.ABORTED; //po to by uzyskac 2 efekt.. z tresci zadania...
        task.interrupt();
    }

Teraz spróbuję zająć się wątkiem przerywającym ... jak rozumiem ma on wywolac abort() "odliczać" sekundę po czym, wywływać stary() na watku konkatenującym a następnie ??
W sensie jakby ustawic flage interrupt na false...

Btw. nie jestem zbyt mocny ze współbieżnego i chyba w wolnym czasie zarzucę sobie jakąś książkę... ktoś może jakąś polecić ? :(

Edit: Nawet jeżeli ktoś odpowie jutro to chętnie i tak dowiem się jak to należało zrobić ;)))

edytowany 1x, ostatnio: Chungu, 2017-12-08 16:11
No tam generalnie było dużo po poprawy, te 2 konstruktory, volatile zmienna, niektóre instrukcje w pętlach też bez sensu - dużo pisać. Wstrzymaj się chwilę z tą książką, zaraz zaczniecie przerabiać java.util.concurrent i tam są wykonawcy i zupełnie inne podejście do pisania współbieżnego. Nieużywany narząd zanika, więc przerobisz taką Java Concurrency in Practice, coś tam zapamiętasz ale o ile nie będziesz tego na co dzień praktykować (a prawdopodobnie nie będziesz) to szybko wszystko zapomnisz. Poczytaj jak pracować z ExecutorService, Runnable, Callable i tyle póki co :) - pedegie 2017-12-09 09:03
Ok, dzięki ;) - Chungu 2017-12-11 18:30

Pozostało 580 znaków

2017-12-08 16:51
0

Dobra, odnośnie wątku przerywającego zmajstrowałem coś takiego:

import java.lang.Thread;

public class InterruptionThread extends Thread {

    private StringTask taskToAbort;
    private Thread nakedThread;

    public InterruptionThread(StringTask taskToAbort) {
        // TODO Auto-generated constructor stub
        this.taskToAbort=taskToAbort;
        this.nakedThread=taskToAbort.getThread();
    }

    @Override
    public void run() {
        //watek najpierw zatrzymuje(ustawia flage interrupt) wykonywanie zadania po sekundzie
        try {
            Thread.sleep(1000);
            taskToAbort.abort();
            taskToAbort.getThread().interrupted();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            //e.printStackTrace();
            return ;
        }

    }

}

W klasie Main:

// ...
if (args.length > 0 && args[0].equals("abort")) { 
    /*<- tu zapisać kod  przerywający działanie tasku po sekundzie 
         i uruchomić go w odrębnym wątku
    */
        //Thread.sleep(1000);
        InterruptionThread interThread=new InterruptionThread(task);
        interThread.start();
        //task.abort();
        //StringTask task2=new StringTask(task.getStringToMultiply(),task.getHowMany(),task.getResult());

    }
//...

Działa (przynajmniej dla tych kilkunastu prób przeprowadzonych na moim kompie ;) ) ale mam wrażenie, że takie sposób uruchamiania tego wątku jest trochę zły... poza tym w eclipse mam jakieś warningii w klasie IterruptionThread .. Pozdrawiam :)

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0