Esperimenti con semafori e monitor in Python
Ho messo a vostra disposizione alcuni sorgenti che implementano in Python 3 le astrazioni di semaforo e monitor come le abbiamo viste (o vedremo presto) a lezione. Per il supporto di multithreading si usa la libreria standard threading.
Anche Python (purtroppo) definisce semafori non fair e variabili di condizione di tipo signal&continue. Questi sorgenti implementano (SE&O) semafori fair e monitor con condizioni signal-urgent.
Ho messo il materiale in questo file.
Semafori
Il sorgente semaphore.py definisce la classe semaphore che ha due metodi P() e V() (non l'avreste mai detto...).
Quindi un semaforo mutex si definisce
mutex = semaphore(1)
e si usa così
mutex.P()
# critical section
mutex.V()
produttore consumatore con i semafori
Il file pc_sema.py implementa una soluzione del problema dei produttori/consumatori in python.
Per evitare di diver scrivere come prefisso il nome del modulo (semaphore.semaphore) ho messo l'import che segue:
from semaphore import semaphore
La funzione safeprint serve per poter ricevere stampe leggibili perché la print stessa non è atomica. (usano una print al posto della safeprint si possono osservare le stampe dei diversi thread mischiate fra loro).
mutex = semaphore(1)
def safeprint(*args,**kwargs):
mutex.P()
print(*args,**kwargs)
mutex.V()
Le variabili condivise (globali) sono:
buf = []
full = semaphore(0)
empty = semaphore(1)
La struttura del produttore e del consumatore è la seguente:
def producer():
while True:
# produce val
empty.P()
buf.append(val)
full.V()
def consumer():
while True:
full.P()
val = buf.pop(0)
empty.V()
# consume val
(notate che basta cambiare il valore iniziale del semaforo empty per far diventare questo programma una soluzione del buffer limitato, sebbene con un solo produttore e un solo oconsumatore.
Monitor in Python
Il sorgente monitor.py implementa le classi monitor, condition e il decoratore entry.
Per evitare il prefisso del modulo consiglio di importare monitor.py come segue:
from monitor import monitor, condition, entry
Per definire un modulo basta derivare la classe monitor:
class mymonitor(monitor):
def __init__(self):
super().__init__(self)
# definizione degli attributi dell'oggetto per esempio una var. di condizione
mycond = condition(self)
Le variabili di condizione hanno come unico parametro nel costruttore il monitor al quale fanno riferimento.
Per definire una procedure entry occorre mettere il decoratore entry alla funzione:
@entry
def my_procedure_entry(self, ....args....):
# ... implementation of my_procedure_entry
Sulle variabili di condizione sono definiti i metodi wait() e signal().