02 BS – Prozesse_u_Threads

Werbung
Betriebssysteme
Wintersemester 2015
Kapitel 2 – Prozess und Threads
Patrick Kendzo
[email protected]
Programm
Inhalt
Einleitung
Prozesse und Threads

Speicherverwaltung
Eingabe und Ausgabe
Dateisysteme
Zusammenfassung + Klausurvorbereitung
2
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Wiederholung Synchronization von JavaThreads (1)
☞ Einsatz vom Synchronized-Mechanismus zur Synchronisation paralleler Threads unzureichend

Beispielszenario: Parkhaus
 Ein Parkhaus besteht aus N Parkplätzen
 Einfahr nur möglich, falls weniger als N Autos im Parkhaus sind, sonst muss man warten
 Fährt ein Auto aus, kann ein evt. wartendes Auto einfahren

Umsetzung in Java:
(1)
(2)
(3)
(4)
(5)
3
Klasse mit Attribut für die Anzahl freier Plätze initialisiert mit N
Methoden zum Herunter- und Heraufzählen (für Einfahren bzw. Ausfahren) des Attributs N
Da N von mehreren Threads (Autos) benutzt wird, müssen diese Methoden synchronized sein
Beachte: Falls N = 0 ist, kann es nicht heruntergezählt werden (es kann nicht weniger als 0 freie
Plätze geben), sondern das Auto muss warten (möglichst nicht mit aktivem Warten realisiert)
Falls das Attribut N wieder hochgezählt wird, muss ein evtl. wartender Thread benachrichtigt
werden, dass jetzt eine Chance besteht, das Attribut herunterzuzählen
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Wiederholung Synchronization von JavaThreads (2)
Beispielszenario: Parkhaus (continued)

 Lösungsanalyse
Versuch 3: Nutzung von wait() und notify()

 wait() und notify() sind Methode der Klasse Objekt (werden daher von jeder Klasse geerbt)
 public final void wait() throws InterruptedException;
 public final void notify();
Beachte: wait() und notify() sind nur aus synchronized-Methoden aus aufrufbar!
 Sie müssen auf ein Objekt angewendet werden, das in diesem Augenblick durch
synchronized gesperrt ist

 Sonst: Ausnahme IllegalMonitorStateException
4
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Wiederholung Synchronization von JavaThreads (3)

Beispielszenario: Parkhaus (continued)
 Lösungsanalyse

Versuch 3: Nutzung von wait() und notify() (continued)
 Folge:
(1) Bei wait() wird Sperre auf das Objekt aufgegeben und der Thread wird blockiert
(2) notify() ermöglicht irgendeinem an diesem Objekt wartenden Thread weiterzulaufen
(3) Falls momentan kein Thread an diesem Objekt wartet, so hat der Aufruf von notify keine
Wirkung
(4) Falls ein durch wait() wartender Thread weiterlaufen kann, weil er von notify ausgewählt
wurde, so muss er das Objekt vor dem Weiterlaufen erneut sperren
☞ Alternative wait()-Varianten
public final void wait(long timeout)
throws InterruptedException;
public final void wait(long timeout, int nanos)
throws InterruptedException;
5
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Wiederholung Synchronization von JavaThreads (4)
Erzeuger-Verbraucher-Problem

 Grenzen von wait() und notify()
Beispielszenario: Modellierung einer Datenübergabe



Ein Thread (Produzent, Erzeuger) legt Daten (z.B. Zahl) in einem Puffer ab ( z.B. ein neuer Messwert - Temperatur,
Geschwindigkeit usw.)
Ein anderer Thread (Konsument, Verbraucher) holt Daten (z.B. Zahl) aus einem Puffer heraus (zB. zum anzeigen,
Mittelwertberechnung, Speichern)
Ziele:

 Jeder Messwert soll nur einmal angezeigt (bzw. gelesen) werden
 Es soll kein Messwert verloren gehen
 Es soll aktives Warten vermieden werden
Versuch 1: Realisierung mit synchronized – wait - notify
Beobachtung:
 Falls genau ein Produzent und ein Verbraucher vorhanden sind, dann funktioniert eine Lösung mit synchronized ,
wait() und notify()
 Falls mehrere Produzenten und Konsumenten vorhanden sind, so bleiben diese nach einer gewissen Zeit
(nichtdeterministisch) stehen


Lösung: Verwendung von notifyAll()

 Alle Threads , die auf die Freigabe der Sperre auf ein Objekt warten werden geweckt...
6
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Synchronization von Java-Threads (1)
Erzeuger-Verbraucher-Problem (continued)

 Mit wait und notifyAll
7
class Buffer {
private boolean available = false;
private int data;
public synchronized void put(int x) {
while(available) {
try{
wait();
}catch(InterruptedException e) {}
}
data = x;
available = true;
notifyAll();
}
public synchronized int get(){
while(!available) {
try{
wait();
}catch(InterruptedException e) {}
}
available = false;
notifyAll();
return data;
}
}
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Synchronization von Java-Threads (2)
Erzeuger-Verbraucher-Problem (continued)

 Mit wait und notifyAll
class Producer extends Thread{
private Buffer buffer;
private int start;
public Producer(Buffer b, int s){
buffer = b; start = s;
}
public void run(){
for(int i = start; i < start + 100; i++){
buffer.put(i);
}
}
}
8
class Consumer extends Thread{
private Buffer buffer;
public Consumer(Buffer b){
buffer = b;
}
public void run(){
for(int i = 0; i < 100; i++){
int x = buffer.get();
System.out.println("got " + x);
}
}
}
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Synchronization von Java-Threads (3)
Erzeuger-Verbraucher-Problem (continued)

 Mit wait und notifyAll
public class ProducerConsumer{
public static void main(String[] args){
Buffer b = new Buffer();
Consumer c1 = new Consumer(b);
Consumer c2 = new Consumer(b);
Consumer c3 = new Consumer(b);
Producer p1 = new Producer(b, 1);
Producer p2 = new Producer(b, 101);
Producer p3 = new Producer(b, 201);
c1.start();
c2.start();
c3.start();
p1.start();
p2.start();
p3.start();
}
}
9
Betriebssysteme Wintersemester 2015
Ausgabe:
...
got 98
...
got 97
...
© Patrick Kendzo
Synchronization von Java-Threads (4)
Nutzung von notifyAll

 notifyAll() ist eine Methode der Klasse Objekt (wie wait() und notify())
 public final void notifyAll();
Beachte:
(1)
Genau wie bei wait() und notify() ist notifyAll() nur aus synchronized-Methoden aufrufbar!
(2)
notifyAll() kann immer statt notify() verwendet werden
 Korrektheit ist nicht betroffen, aber Effizienz
notifyAll() muss in folgenden Fällen statt notify() verwendet werden:
(3)
a)
 Es können mehrere wartende Threads existieren, die wegen unterschiedlicher
Bedingungen warten (z.B.: Erzeuger-Verbraucher-Problem)
b)
 Durch die Zustandsänderung eines Objekts können die Wartebedingung mehrerer
wartenden Threads hinfällig werden (z.B.: Verkehrsampel)
10
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Synchronization von Java-Threads (5)

Nutzung von notifyAll (continued)
Wirkung:
 notifyAll() ermöglicht allen an diesem Objekt wartenden Threads
weiterzulaufen (Aber nicht allen in einem Prozess vorhandenen Threads!)
 Falls momentan kein Thread an diesem Objekt wartet, so ist der Aufruf von
notifyAll() wirkungslos
11
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Prozesszutänden (1)
☞ Ein Prozess kann sich in den Zuständen rechnend, rechenbereit und blockiert
befinden
Rechnend
1
2
3
Blockiert
Rechenbereit
1.
Prozess blockiert, weil er z.B. auf EA wartet
2.
Scheduler wählt einen anderen Prozess aus
3.
Scheduler wählt diesen Prozess aus
4.
Eingaben vorhanden
4
Quelle: vgl.Tanenbaum
12
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Prozesszutänden (2)
☞ Zustandsübergangsdiagramm für Java-Threads
Quelle: vgl. Oechsle
13
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (1)
☞ Synchronisationsmechanismen von Java (synchronized, wait, notify,
notifyAll) werden an Beispielen angewendet
Hintergrund:
-
Bis jetzt Synchronisation von Threads innerhalb eines Prozesses. Mit Zugriff auf gemeinsame Objekte
-
-
-
 Thread teilen sich den selben Adressraum (Metapher: Köche in einer gemeinsamen Küche)
Synchronisations- und Kommunikationsmechanismen in gängiger BS
-
 Finden vorwiegen zwischen Prozessen statt
-
 Prozesse besitzen keine gemeinsamen Adressraum
Die Funktionalität der Konzepte werden in einer objektorientierten Weise (Java) nachgeahmt
„klassische“ Konzeptbeispiele: (sind alle dem BS UNIX entlehnt)

Semaphore (klassische Synchronisation)

Message Queues (klassische Kommunikation)

Pipes (klassische Kommunikation)
14
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (2)
☞ Synchronisationsmechanismen von Java (synchronized, wait, notify,
notifyAll) werden an Beispielen angewendet (continued)
„klassische“ Synchronisationsaufgaben:

Werden in Lehrbüchern zur Illustration von Synchronisationskonzepten herangezogen

Beispiele:

Philosophen-Problem

Leser-Schreiber-Problem
15
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (3)
☞ Einfache Semaphore

Klassische Form der Synchronisation in Betriebssystemen (schon als Parkhaus bekannt)
public class Semaphore{
private int value;
public Semaphore(int init){
if(init < 0)
init = 0;
value = init;
}
public synchronized void p(){
while(value == 0){
try{
wait();
}catch(InterruptedException e) {}
}
value--;
}
public synchronized void v(){
value++;
notify();
}
}
16
Bisher Parkhaus
Bisher plätze
Betriebssysteme Wintersemester 2015
Bisher passieren() / enter()
Bisher verlassen() / leave()
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (4)
☞ Einfache Semaphore (continued)

Anwendungsbereiche:

 Gegenseitiger Ausschluss (Mutual Exclusion)


Bedeutung: zu einem Zeitpunkt t kann ein bestimmtes Programmstück (kritischer Abschnitt) von
höchstens einem Thread ausgeführt werden (snychronized in Java)

In Kontext von Threads aus unterschiedlichen Prozessen stellt den Betriebssystemkern ein Semaphor
(Mutex-Semaphore) zur Verfügung, die als Parameter eines Systemaufrufs mit übergeben wird.
 Herstellung vorgegebener Ausführungsreihenfolgen von Aktionen in unterschiedlichen
Threads
a2
a1
a3
a5
a4
17
Betriebssysteme Wintersemester 2015
Zeitliche Relation zwischen den
Aktionen:
a1 vor a2
a2 und a3 parallel
...
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (5)
☞ Additive Semaphore

Verallgemeinerung von einfachen Semaphoren
 Bis jetzt wurde der Semaphorenwert nur um eins erhöht oder erniedrigt
 Mit Additiver Semaphore kann dieser um beliebige Werte erhöht oder
erniedrigt werden
18
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (6)
☞ Additive Semaphore (continued)
public class AdditiveSemaphore{
private int value;
public AdditiveSemaphore(int init){
if(init < 0)
init = 0;
this.value = init;
}
public synchronized void p(int x){
if(x <= 0) // x muss positiv sein! Warum?
return;
while(value - x < 0){
try{
wait();
}catch(InterruptedException e) {}
}
value -= x;
}
public synchronized void v(int x){
if(x <= 0) // x muss positiv sein! Warum?
return;
value += x;
notifyAll(); // nicht notify
}
19
Betriebssysteme Wintersemester 2015
public void p(){ // wie bisher
p(1);
}
public void v(){// wie bisher
v(1);
}
public void change(int x){
if(x > 0)
v(x); // erhöht um x
else if(x < 0)
p(-x); // erniedrigt um x
}
}
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (7)
☞ Additive Semaphore (continued)

Schrittweises Erniedrigen vs. Erniedrigen „auf einen Schlag“
 Der Aufruf „p(3)“ ist nicht gleich 3 aufeinander folgende Aufrufe „p(1)“ bzw. „p()“

Beispiel

20
Gegeben:

Additiver Semaphor „S“ mit Wert „4“ und

zwei Threads („T1“ und „T2“), die gemeinsam S benutzen

Annahme: S soll von T1 und T2 jeweils um 3 erniedrigt werden

Schrittweises Erniedrigen ( Aufruffolge von p(1)) kann zu Verklemmung führen!

Beim Erniedrigen auf einen Schlag entsteht keine Verklemmung.
 Ein Thread kann erniedrigen, der Andere muss warten
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (8)
☞ Semaphorgruppen

Verallgemeinerung der Additiven Semaphoren
 In UNIX werden diese Gruppen einfach Semaphore genannt
 Mit Aufruf der change-Methode können mehrere Semaphore der Gruppe „auf einen
Schlag“ verändert werden
 Änderungen werden dann nur durchgeführt, wenn nach Änderung alle Semaphore-Werte der
Gruppe nicht negativ sind
 Sonst wird gewartet (Alles oder Nichts!)
21
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Klassische Probleme der
Interprozesskomunikation (9)
☞ Semaphorgruppen
public class SemaphoreGroup{
private int[] values;
public SemaphoreGroup(int numberOfMembers){
if(numberOfMembers <= 0)
return;
values = new int[numberOfMembers];
}
public int getNumberOfMembers(){
return values.length;
}
public synchronized void changeValues(int[] deltas){
if(deltas.length != values.length)
return;
while(!canChange(deltas)){
try{
wait();
}catch(InterruptedException e){}
}
doChange(deltas);
notifyAll();
}
22
private boolean canChange(int[] deltas){
for(int i = 0; i < values.length; i++)
if(values[i] + deltas[i] < 0)
return false;
return true;
}
private void doChange(int[] deltas){
for(int i = 0; i < values.length; i++)
values[i] = values[i] + deltas[i];
}
}
Betriebssysteme Wintersemester 2015
© Patrick Kendzo
Herunterladen