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

Współbieżność oraz zadanie w tle
CH
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 miesiące
  • Postów:128
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)...

Kopiuj
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...

Kopiuj
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:

Kopiuj
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
pedegie
  • Rejestracja:około 11 lat
  • Ostatnio:ponad rok
  • Postów:204
1
Kopiuj
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
CH
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 miesiące
  • Postów:128
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:

Kopiuj
	@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
pedegie
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 :)
CH
Ok, dzięki ;)
CH
  • Rejestracja:ponad 8 lat
  • Ostatnio:około 2 miesiące
  • Postów:128
0

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

Kopiuj
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:

Kopiuj
// ...
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 :)

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)