Difference between revisions of "Esperimenti con semafori e monitor in Python"
(Created page with "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 supp...") |
|||
(6 intermediate revisions by the same user not shown) | |||
Line 25: | Line 25: | ||
Il file ''pc_sema.py'' implementa una soluzione del problema dei produttori/consumatori in python. | 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: | ||
+ | <syntaxhighlight lang=python> | ||
+ | from semaphore import semaphore | ||
+ | </syntaxhighlight> | ||
La funzione ''safeprint'' serve per poter ricevere stampe leggibili perché la print stessa non è atomica. | La funzione ''safeprint'' serve per poter ricevere stampe leggibili perché la print stessa non è atomica. | ||
Line 35: | Line 40: | ||
mutex.V() | mutex.V() | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | |||
+ | Le variabili condivise (globali) sono: | ||
+ | <syntaxhighlight lang=python> | ||
+ | buf = [] | ||
+ | full = semaphore(0) | ||
+ | empty = semaphore(1) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | La struttura del produttore e del consumatore è la seguente: | ||
+ | <syntaxhighlight lang=python> | ||
+ | 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 | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | (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: | ||
+ | <syntaxhighlight lang=python> | ||
+ | from monitor import monitor, condition, entry | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Per definire un modulo basta derivare la classe ''monitor'': | ||
+ | <syntaxhighlight lang=python> | ||
+ | class mymonitor(monitor): | ||
+ | def __init__(self): | ||
+ | super().__init__() | ||
+ | # definizione degli attributi dell'oggetto per esempio una var. di condizione | ||
+ | mycond = condition(self) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 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: | ||
+ | <syntaxhighlight lang=python> | ||
+ | @entry | ||
+ | def my_procedure_entry(self, ....args....): | ||
+ | # ... implementation of my_procedure_entry | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Sulle variabili di condizione sono definiti i metodi ''wait()'' e ''signal()''. | ||
+ | |||
+ | == produttore consumatore con i monitor == | ||
+ | |||
+ | Anche in questo caso è stata scritta una safeprint per avere print in critical section. | ||
+ | È un esempio minimale di uso dei monitor visto che la mutua esclusione viene fornita come caratteristca standard delle procedure entry. | ||
+ | |||
+ | <syntaxhighlight lang=python> | ||
+ | class safeprint(monitor): | ||
+ | def __init__(self): | ||
+ | super().__init__() | ||
+ | |||
+ | @entry | ||
+ | def print(self, *args,**kwargs): | ||
+ | print(*args,**kwargs) | ||
+ | |||
+ | safeprint = safeprint().print | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | La classe pcmon (producer consumer monitor) è la seguente: | ||
+ | <syntaxhighlight lang=python> | ||
+ | class pcmon(monitor): | ||
+ | def __init__(self, size=1): | ||
+ | super().__init__() | ||
+ | self.ok2write = condition(self) | ||
+ | self.ok2read = condition(self) | ||
+ | self.buf = [] | ||
+ | self.size = size | ||
+ | |||
+ | @entry | ||
+ | def put(self, val): | ||
+ | if len(self.buf) >= self.size: | ||
+ | self.ok2write.wait() | ||
+ | self.buf.append(val) | ||
+ | self.ok2read.signal() | ||
+ | |||
+ | @entry | ||
+ | def get(self): | ||
+ | if len(self.buf) == 0: | ||
+ | self.ok2read.wait() | ||
+ | rval = self.buf.pop(0) | ||
+ | self.ok2write.signal() | ||
+ | return rval | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | (anche in questo caso mettendo un parametro al costruttore diventa una soluzione per bounded-buffer. A differenza della soluzione con i semafori questa ammette produttori e consumatori multipli). | ||
+ | |||
+ | La sintassi è molto simile a quella usata negli esercizi teorici. Occorre solo ricordarsi di: | ||
+ | * concatenare il costruttore della classe monitor | ||
+ | * indicare il decoratore entry ad ogni procedure entry (altrimenti non c'è la sezione critica). |
Latest revision as of 19:38, 27 October 2016
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__()
# 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().
produttore consumatore con i monitor
Anche in questo caso è stata scritta una safeprint per avere print in critical section. È un esempio minimale di uso dei monitor visto che la mutua esclusione viene fornita come caratteristca standard delle procedure entry.
class safeprint(monitor):
def __init__(self):
super().__init__()
@entry
def print(self, *args,**kwargs):
print(*args,**kwargs)
safeprint = safeprint().print
La classe pcmon (producer consumer monitor) è la seguente:
class pcmon(monitor):
def __init__(self, size=1):
super().__init__()
self.ok2write = condition(self)
self.ok2read = condition(self)
self.buf = []
self.size = size
@entry
def put(self, val):
if len(self.buf) >= self.size:
self.ok2write.wait()
self.buf.append(val)
self.ok2read.signal()
@entry
def get(self):
if len(self.buf) == 0:
self.ok2read.wait()
rval = self.buf.pop(0)
self.ok2write.signal()
return rval
(anche in questo caso mettendo un parametro al costruttore diventa una soluzione per bounded-buffer. A differenza della soluzione con i semafori questa ammette produttori e consumatori multipli).
La sintassi è molto simile a quella usata negli esercizi teorici. Occorre solo ricordarsi di:
- concatenare il costruttore della classe monitor
- indicare il decoratore entry ad ogni procedure entry (altrimenti non c'è la sezione critica).