Prof. Dr. rer. nat. Roland Wism ¨uller
Aufgabenblatt 5 Musterl¨osung
Vorlesung Betriebssysteme I Wintersemester 2021/22
Aufgabe 1: Implementierung von Threads
Der gr¨oßte Vorteil ist die Effizienz. Es werden keine Einspr¨unge in den Betriebssystem-Kern ben¨otigt, um eine Thread- wechsel vorzunehmen. Der gr¨oßte Nachteil ist, daß der ganze Prozeß blockiert, sobald einer der Threads blockiert.
Aufgabe 2: POSIX Threads
Die Reihenfolge in der Ausgabe unterscheidet sich von Lauf zu Lauf und ist nicht vorhersehbar. Die Threads betreiben keinerlei Reihenfolgesynchronisation, d.h. das Betriebssystem kann die Threads in beliebiger Reihenfolge ausf¨uhren.
Insbesondere istsleepkein geeignetes Mittel zur Synchronisation.
#include <unistd.h> // for sleep
#include <stdio.h> // for printf
#include <stdlib.h> // for exit()
#include <pthread.h> // POSIX threads
#include <string.h> // string functions: strcpy
void * print_message(void *ptr); // prototype for thread routine typedef struct _threadData
{ // data to be passed to a thread
int thread_id;
char message[100];
} threadData;
int main() {
pthread_t thread1, thread2; // thread variables
threadData data1, data2; // own structs to be passed to the threads // set data to pass to first thread
data1.thread_id = 1;
strcpy(data1.message, "Hello!");
// set data to pass to second thread data2.thread_id = 2;
strcpy(data2.message, "Hi!");
// create both threads
if ( (pthread_create(&thread1, NULL, print_message, (void *) &data1) != 0)
|| (pthread_create(&thread2, NULL, print_message, (void *) &data2) != 0)) {
printf("Thread creation failed\n");
exit(1);
1
}
printf("Main thread\n");
/*
Main thread now waits for both threads to terminate, before it exits. If main thread exits, both threads exit, even they have not finished their work (check this by by removing the pthread_join calls.)
*/
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
exit(0);
} /*
* print_message is used as the start routine for the threads used
* Params: ptr - pointer to the thread’s data
*/
void *print_message(void *ptr) {
threadData *data;
data = (threadData *) ptr; // cast to a pointer to threadData // do some job: display a message
int i;
for (i=1; i<20; i++) {
printf("Thread %d says %s \n", data->thread_id, data->message);
sleep(data->thread_id); // sleep for a while ...
}
return NULL;
}
Aufgabe 3: Java Threads
Es l¨asst sich beobachten, dass die Ausgabe unvorhersehbar ist und von Lauf zu Lauf abweicht. In der unten aufgef¨uhrten L¨osung wird jeder Thread nach 50 Iterationen schlafen gelegt (Thread.sleep()). Das bedeutet aber nicht, dass er zuvor 50 Iterationen ununterbrochen gerechnet hat. Nutzt man stattdessenThread.yield()kann man i.d.R. keinen Unterschied in der Ausgabe feststellen (die Ausf¨uhrungszeit kann/sollte aber k¨urzer sein). Tats¨achlich gibt hier der auf- rufende Thread die CPU freiwillig ab, blockiert aber nicht. Er wird lediglich ans Ende der Warteschlange bewegt, bleibt aber bereit. Die MethodeThread.join()kann verwendet werden, um auf einen Thread zu warten, bis dieserfertig ist. Der aufrufende Thread blockiert dabei.
a) // The thread class for printing a specified character
public class DisplayChar implements Runnable {
private char charToDisplay; // The character to print private int times; // The times to repeat // Construct a thread with specified character and number of // times to print the character
public DisplayChar(char c, int t) {
charToDisplay = c;
times = t;
}
2
// Implement the run() method public void run() {
for (int i = 0; i < times; i++) { // Zum Ausprobieren: Threads schlafen legen // Versuchen Sie auch Thread.yield() statt sleep().
if (i == 50) {
System.out.print(Character.toUpperCase(charToDisplay));
try {
Thread.sleep(100);
}
catch (InterruptedException e) {}
} else {
System.out.print(charToDisplay);
}
// Wichtig, damit Ausgaben sofort erscheinen System.out.flush();
} }
public static void main(String[] args) {
Thread tA = new Thread(new DisplayChar(’a’, 100));
Thread tB = new Thread(new DisplayChar(’b’, 100));
Thread tC = new Thread(new DisplayChar(’c’, 100));
Thread tD = new Thread(new DisplayChar(’d’, 100));
// Start threads tA.start();
tB.start();
tC.start();
tD.start();
// hier sollte man eigentlich die erzeugten Threads joinen }
}
Aufgabe 4: Threads mit gemeinsamen Variablen
// Diese Klasse enth¨alt eine Integer Variable, die von allen // Threads genutzt werden kann.
class CommonVar {
public int i;
public CommonVar() {
i = 0;
} }
// Thread-Klasse
public class Test extends Thread {
private CommonVar cvar;
3
// Konstruktor initialisiert Referenz auf gemeinsame Variable public Test(CommonVar c)
{
cvar = c;
}
// Implementierung der run() Methode public void run()
{
for (int i = 0; i < 1000000; i++) { cvar.i++;
} }
public static void main(String[] args) throws InterruptedException {
CommonVar c = new CommonVar();
Thread t1 = new Test(c);
Thread t2 = new Test(c);
// Starte Threads t1.start();
t2.start();
// Warte auf Thread-Ende t1.join();
t2.join();
// Drucke c.i
System.out.println(c.i);
} }
Programm ausf¨uhren:
bslab01% java Test 1019051
bslab01% java Test 2000000
bslab01% java Test 1560671
Der Prozessor f¨uhrt das Hochz¨ahlen in drei Schritten durch: erst wird der alte Wert aus dem Speicher geladen, dann wird er hochgez¨ahlt, dann wieder in den Speicher geschrieben. Es kann daher vorkommen, daß zwei Threads (fast) gleichzeitig den alten Wert holen (also bevor der andere Thread ihn wieder speichern konnte). Dann geht mindestens eine ¨Anderung (also eine Inkrementierung) verloren, so daß das Ergebnis kleiner als 2000000 wird.
4