Difference between revisions of "Esperimenti con semafori e monitor in Python"

From Sistemi Operativi
Jump to navigation Jump to search
(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&eacute; la print stessa non &egrave; atomica.
 
La funzione ''safeprint'' serve per poter ricevere stampe leggibili perch&eacute; la print stessa non &egrave; 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 &egrave; 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 &egrave; stata scritta una safeprint per avere print in critical section.
 +
&Egrave  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) &egrave; 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 &egrave; 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'&egrave; 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. &Egrave 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).