Vinnaren i pepparkakshustävlingen!
  • 1
  • 2
2020-10-15, 22:25
  #1
Medlem
Hej. Jag får inte riktigt kläm på det här.

Jag skapar två trådar som har tillgång till en monitor med "synchronized"-funktioner. Nu är det ju så att båda trådar kör, men de kör hela sin grej efter varandra. Dvs varje for-loop kör tills den är slut. Jag hade i stället önskat att de hade alternerat. Testat olika saker som att lägga till en semafor som skulle få den andra att köra och vice versa, men jag lyckas inte lösa det.
Koden är som följer.

Kod:
public class Main {
		
	public static void thread1(String name, Monitor mon) {
		
		for (int i = 0; i < 20; i++) {
			mon.sub();
		}

	}


	public static void thread2(String name, Monitor mon) {
		
		for (int i = 0; i < 20; i++) {
			mon.add();
		}
	}


	public static void main(String[] args) throws InterruptedException {
		// TODO Auto-generated method stub
		Monitor mon = new Monitor();

		Thread t1 = new Thread(()-> thread1("t1", mon));
		Thread t2 = new Thread(()-> thread2("t2", mon));
		
		t1.start();
		t2.start();
		t1.join();
		t2.join();
		
		System.out.println( "\nfinal balance is now: " + mon.get() );
	}
	
}


Kod:
public class Monitor {

	private int balance = 0;

	public synchronized void add() {
		balance += 1;
		System.out.println(Thread.currentThread().getName() + " adds 1 and balance is now : " + balance);
	}
	
	public synchronized void sub() {
		balance -= 1;
		System.out.println(Thread.currentThread().getName() + " subtracts 1 and balance is now : " + balance);
	}
	
	public synchronized int get() {
		return balance;
	}
}
Citera
2020-10-15, 22:30
  #2
Medlem
Trollfeeders avatar
Java är väl inte direkt rätt val av språk för realtidsprogrammering till att börja med. Garbage cleaning gör språket värdelöst för sådant.
Citera
2020-10-15, 22:33
  #3
Medlem
Citat:
Ursprungligen postat av Trollfeeder
Java är väl inte direkt rätt val av språk för realtidsprogrammering till att börja med. Garbage cleaning gör språket värdelöst för sådant.
Jasså? Kan du utveckla? Inte hört om just det tidigare. Däremot kan jag inte göra något åt det nu eftersom kursen just är realtidsprogrammering i Java.
Citera
2020-10-15, 23:02
  #4
Medlem
Trollfeeders avatar
Citat:
Ursprungligen postat av dagsvag
Jasså? Kan du utveckla? Inte hört om just det tidigare. Däremot kan jag inte göra något åt det nu eftersom kursen just är realtidsprogrammering i Java.

Det går ju att göra, det är bara ett märkligt val av språk. Eller medvetet för att det blir utrymme för att lära sig. Du kommer nämligen behöva planera garbage collections så att dom inte hamnar vid fel tillfällen.

I realtidsprogrammering är målet att en uppgift ska vara slutförd före en deadline. Det kan t ex vara att ABS-funktionen ska slå till inom ett visst tidsintervall från att man trycker på bromspedel i sin bil, eller att varje frame i en videoström ska hinna processas inom ett visst fönster, annars laggar bilden. Om vi tar det första exemplet, så kan det bli väldigt tråkigt om garbage collectorn sätter igång just precis när man trampar på bromsen i halt väglag i en knivig situation och hjulen låser sig i en halv sekund medan den står och tuggar. Just i det exemplet så är Java helt värdelöst och något du inte vill använda alls. Men i fallet med videoströmmen, så kanske du får indata lite ojämnt, och då skulle du kunna trigga garbage collectorn dom gånger du märker att du är klar så tidigt i en frame att den hinner köra klart i tid så att du hinner avkoda nästa frame i tid.
Citera
2020-10-15, 23:09
  #5
Medlem
JohannesSnajdares avatar
Jag tror att 20 iterationer är lite för kort för att "timeslicen" ska hinna "kicka in", d.v.s. tråden hinner bli klar innan det är dags att växla körandet till den andra tråden.

Öka på till 200000 iterationer och se vad du får för resultat.
Citera
2020-10-15, 23:16
  #6
Medlem
JohannesSnajdares avatar
Alternativt kläm in en liten "sleep" i looparna så får du ditt önskade resultat...

Kod:
        public static void thread1(String name, Monitor mon) {

                for (int i = 0; i < 20; i++) {
                        mon.sub();
                        try {
                        Thread.sleep(100);
                        } catch(InterruptedException e) {}
                }

        }


        public static void thread2(String name, Monitor mon) {

                for (int i = 0; i < 20; i++) {
                        mon.add();
                        try {
                        Thread.sleep(100);
                        } catch(InterruptedException e) {}
                }
        }
Citera
2020-10-15, 23:17
  #7
Medlem
Citat:
Ursprungligen postat av Trollfeeder
Det går ju att göra, det är bara ett märkligt val av språk. Eller medvetet för att det blir utrymme för att lära sig. Du kommer nämligen behöva planera garbage collections så att dom inte hamnar vid fel tillfällen.

I realtidsprogrammering är målet att en uppgift ska vara slutförd före en deadline. Det kan t ex vara att ABS-funktionen ska slå till inom ett visst tidsintervall från att man trycker på bromspedel i sin bil, eller att varje frame i en videoström ska hinna processas inom ett visst fönster, annars laggar bilden. Om vi tar det första exemplet, så kan det bli väldigt tråkigt om garbage collectorn sätter igång just precis när man trampar på bromsen i halt väglag i en knivig situation och hjulen låser sig i en halv sekund medan den står och tuggar. Just i det exemplet så är Java helt värdelöst och något du inte vill använda alls. Men i fallet med videoströmmen, så kanske du får indata lite ojämnt, och då skulle du kunna trigga garbage collectorn dom gånger du märker att du är klar så tidigt i en frame att den hinner köra klart i tid så att du hinner avkoda nästa frame i tid.

Tack för förklaringen.

Citat:
Ursprungligen postat av JohannesSnajdare
Jag tror att 20 iterationer är lite för kort för att "timeslicen" ska hinna "kicka in", d.v.s. tråden hinner bli klar innan det är dags att växla körandet till den andra tråden.

Öka på till 200000 iterationer och se vad du får för resultat.

Tack, ja det gjorde skillnad. Då förstår jag och då funkar det väl som det ska. För syns skull hade det bara sett snyggare ut om den bytte oftare, så gott som varannan. Hur hade man kunnat göra det?
__________________
Senast redigerad av dagsvag 2020-10-15 kl. 23:21.
Citera
2020-10-15, 23:19
  #8
Medlem
Citat:
Ursprungligen postat av JohannesSnajdare
Alternativt kläm in en liten "sleep" i looparna så får du ditt önskade resultat...

Kod:
        public static void thread1(String name, Monitor mon) {

                for (int i = 0; i < 20; i++) {
                        mon.sub();
                        try {
                        Thread.sleep(100);
                        } catch(InterruptedException e) {}
                }

        }


        public static void thread2(String name, Monitor mon) {

                for (int i = 0; i < 20; i++) {
                        mon.add();
                        try {
                        Thread.sleep(100);
                        } catch(InterruptedException e) {}
                }
        }
Ah, Tack!, det gjorde det lite mer uppenbart, men om jag inte vill ha en sleep där. Säg att jag inte vill att programmet ska saktas ner.
Citera
2020-10-15, 23:44
  #9
Medlem
JohannesSnajdares avatar
Då kan du använda två semaforer med en "permit count" på 1, den ena för den ena tråden och den andra för den andra, sen får tråd1 "släppa fram" tråd2 när den är klar med en iteration och väntar därefter på sin semafor tills tråd två är klar och "släpper fram" tråd1 osv osv osv.

i kod skulle den kunna se ut såhär typ:

Kod:
public class Main {

        static Semaphore thread1go = new Semaphore(1, true);
        static Semaphore thread2go = new Semaphore(1, true);

        public static void thread1(String name, Monitor mon) {

                for (int i = 0; i < 20; i++) {
                        try {
                        thread1go.acquire();
                        mon.sub();
                        thread2go.release();
                        } catch(InterruptedException e) {}
                }

        }

        public static void thread2(String name, Monitor mon) {

                for (int i = 0; i < 20; i++) {
                        try {
                        thread2go.acquire();
                        mon.add();
                        thread1go.release();
                        } catch(InterruptedException e) {}
                }
        }

        public static void main(String[] args) throws InterruptedException {
                // TODO Auto-generated method stub
                Monitor mon = new Monitor();

                // se till att tråd två blockas från början
                thread2go.acquire();

                Thread t1 = new Thread(()-> thread1("t1", mon));
                Thread t2 = new Thread(()-> thread2("t2", mon));

                t1.start();
                t2.start();
                t1.join();
                t2.join();

                System.out.println( "\nfinal balance is now: " + mon.get() );
        }

}

Du kanske skulle kunna klara dig på en semafor och låta varje tråd göra acquire/(, mon.add/mon.sub, release(), det funkar förmodligen men du är inte _garanterad_ att de kommer köras växelvis, det _kan_ bli så att samma tråd snor semaforen nästa runda.
Citera
2020-10-16, 00:04
  #10
Medlem
Citat:
Ursprungligen postat av JohannesSnajdare
Då kan du använda två semaforer med en "permit count" på 1, den ena för den ena tråden och den andra för den andra, sen får tråd1 "släppa fram" tråd2 när den är klar med en iteration och väntar därefter på sin semafor tills tråd två är klar och "släpper fram" tråd1 osv osv osv.

Du kanske skulle kunna klara dig på en semafor och låta varje tråd göra acquire/(, mon.add/mon.sub, release(), det funkar förmodligen men du är inte _garanterad_ att de kommer köras växelvis, det _kan_ bli så att samma tråd snor semaforen nästa runda.
Tack! Jo, jag lade in en sån, men det funkade alltså inte, men inser nu att jag var tvungen att ha 2 stycken semaforer för att det ska fungera. Så de låser upp varandra. Det som sätter lite stopp för mig är att om du tar en acquire så har du semaforen, men det fungerar väl så att när du väl lämnar loopen så är den förbrukad och du behöver ta en ny nästa gång?
Tack så mycket!
Nu är det mycket klarare.
Citera
2020-10-16, 09:58
  #11
Medlem
JohannesSnajdares avatar
Se semaforen som en simpel trave med "passerkort".
acquire() plockar ett passerkort från traven och kör vidare. Är traven tom på passerkort så väntar acquire() tills det läggs till (minst) ett passerkort.
release() är funktionen som lägger till ett passerkort på traven.

/JS
Citera
2020-10-16, 10:39
  #12
Medlem
JohannesSnajdares avatar
Btw, så lämnade jag en liten "förbättringspotential" i mitt kodexempel som du nog bör fixa innan du ev. skickar in detta till lärare e.dyl. ;-)

Kodraden:
Kod:
                // se till att tråd två blockas från början
                thread2go.acquire();

kan tas bort om man gör en liten förändring som visar att man förstår hur semaforer funkar ;-)

I all välmening,
JS
Citera
  • 1
  • 2

Stöd Flashback

Flashback finansieras genom donationer från våra medlemmar och besökare. Det är med hjälp av dig vi kan fortsätta erbjuda en fri samhällsdebatt. Tack för ditt stöd!

Stöd Flashback