0 BACI (cz. 3)
Tomasz Zaworski edited this page 2020-11-15 02:28:24 +01:00
  1. Monitory
  2. Przykład monitora
  3. Zadania domowe

Monitory

Monitor to zebrane w jednej konstrukcji programowej pewne wyróżnione zmienne oraz funkcje działające na tych zmiennych. Funkcje są udostępnione na zewnątrz monitora. Tylko ich wywołanie umożliwia procesom dostęp do zmiennych ukrytych wewnątrz monitora.

Cechą wyróżniającą monitora jest to, iż w danej chwili tylko jeden proces może wykonywać jego funkcję. Oprócz tego istnieje możliwość wstrzymywania i wznawiania procesów wewnątrz funkcji monitora. Służą do tego zmienne specjalnego typu condition. Na zmiennych tego typu można wykonywać dwie operacje:

  • void waitc(condition c): powoduje wstrzymanie procesu wykonującego tę operację i wstawienie go na koniec kolejki związanej ze zmienną c, a także jednoczesne zwolnienie monitora,

  • void signalc(condition c powoduje wznowienie któregoś z wstrzymanych procesów oczekujących w kolejce związanej ze zmienną c. Jeśli w chwili wywołania operacji signalc żaden proces nie czeka w kolejce to operacja ta nie ma efektu.

Poza tym istnieje możliwość testowania kolejki związanej ze zmienną typu condition. Uzyskuje się to poprzez wywołanie funkcji int empty(condition c), która zwraca wartość 1, gdy kolejka jest pusta, a wartość 0 w przeciwnym razie.

Przykład monitora

monitor simple_monitor 
{
	void say_hello (char id) 
	{ 
		cout << "Hi! I am a process!" << endl; 
		cout << "My ID is: " << id << endl;
		cout << "Bye!" << endl;
	} 
} 

void process_hello(char id)
{ 
	say_hello(id); 
} 

main() 
{ 
	cobegin 
	{ 
		process_hello('A'); process_hello('B'); process_hello('C'); 
	} 
	cout << "All processes finished" << endl; 
}

Zmienne w monitorze można definiować i inicjalizować w następujący sposób:

monitor monitor_with_variables
{
	int suma;
	// blok init musi być zawsze na samym końcu kodu monitora!
	init 
	{ 
		suma = 0;
	} 
}
Zadanie 1

Użyj monitora do ponownego rozwiązania Zadania 2 z pierwszych zajęć BACI: Dwa procesy w pętli pięciokrotnie inkrementują wartość zmiennej suma. Po zakończeniu procesów, program w funkcji main wypisuje sumę na standardowe wyjście. Wcześniej suma była zmienną globalną - teraz powinna być zmienną ukrytą w monitorze.

Zadanie 2

Napisz program, w którym 2 procesy generują po jednej liczbie, a jeden proces czeka, aż obie liczby zostaną wygenerowane, sumuje je i wypisuje wynik na standardowe wyjście. Do synchronizacji użyj monitora. Wygenerowane wartości powinny być przechowywane wewnątrz monitora.

Zadanie 3

Zaimplementuj rozwiązanie problemu producenta i konsumenta z Zadania domowego 2 z pierwszych zajęć BACI (1 producent, 1 konsument, N liczb do wyprodukowania i wypisania, pojedyncza zmienna typu int do przechowywania wyprodukowanej liczby). W miejsce dwóch semaforów binarnych, użyj monitora i dwóch zmiennych condition Monitor na zewnątrz powinien udostępniać dwie funkcje, np. dodaj_element i pobierz_element


Istnieją rzeczywiste języki programowania obsługujące monitory. (...) Jednym z takich języków jest Java. To język obiektowy obsługujący wątki na poziomie użytkownika. Pozwala również na grupowanie metod (procedur) w klasy. Dzięki dodaniu słowa kluczowego synchronized w deklaracji metody Java gwarantuje, że kiedy dowolny wątek zacznie uruchamiać tę metodę, żaden inny wątek nie będzie mógł uruchomić żadnej innej metody tego obiektu zadeklarowanej ze słowem kluczowym synchronized. Bez słowa kluczowego synchronized nie ma gwarancji przeplatania.

Tanenbaum, A., & Bos, H. (2016). Procesy i wątki. W Systemy operacyjne (p. 162, wyd. 4). Helion.

Zadania domowe

Zadanie domowe 1

Zaimplementuj rozwiązanie problemu producenta i konsumenta dla N-elementowego bufora przy użyciu monitora, bazując na rozwiązaniu zadania 1 z zajęć 10 oraz rozwiązaniu zadania 3 z zajęć dzisiejszych. Monitor powinien jako swoje wewnętrzne zmienne zawierać bufor danych (tablicę) i pozostałe zmienne potrzebne do operacji na tablicy.

Ponieważ funkcje jednego monitora nigdy nie mogą być wykonywane równocześnie przez kilka procesów, takie rozwiązanie nie zapewni możliwości, aby jednocześnie producent zapisywał element a konsument pobierał (inny) element. Z drugiej strony, rozwiązanie to automatycznie zapewni możliwość uruchomienia dowolnej liczby producentów i konsumentów.

W programie uruchom 4 procesy: 3 producentów produkujących po N liczb i 1 konsumenta, który wypisze na standardowe wyjście 3N liczb. (Można uruchomić więcej niż jednego konsumenta - algorytm na to pozwala - ale aby poprawnie zakończyć program w takim wypadku, należy wymyślić, jak konsumenci mają ustalać, że liczby do wypisania już się skończyły.)