108 lines
3.2 KiB
Markdown
Executable file
108 lines
3.2 KiB
Markdown
Executable file
# Primitive di comunicazione asincrone
|
|
|
|
Comunicazione asimmetrica:
|
|
* send non bloccante
|
|
* receive non bloccante
|
|
|
|
I canali sono definiti mediante un costrutto `port`.
|
|
*(vedi lesson 11)*
|
|
|
|
E' la primitiva di piu' basso livello, analogamente ai semafori, soprattutto se il kernel non e' in grado di fornire primitive a memoria condivisa.
|
|
|
|
## Scambio di dati e modello client/server
|
|
|
|
### Producer and Consumer
|
|
|
|
Data una `port dati`:
|
|
|
|
* Il produttore esegue `send() to dati`
|
|
* il consumatore esegue `p=receive() from dati`
|
|
|
|
A differenza di produttore e consumatore nel caso di semafori e monitor, **non c'e' controllo sulla dimensione del canale**. Scambio di dati con buffer **potenzialmente infinito**, che pero' potrebbe essere riempito da applicazioni errate.
|
|
|
|
### Singolo produttore, piu' consumatori
|
|
|
|
*(vedi slides per grafico)*
|
|
Introduce la necessita' di un processo gestore:
|
|
* Il produttore manda i dati sulla porta al gestore
|
|
* I consumatori mandano un segnale **pronto** al gestore, che quindi invia i dati al consumatore attraverso un canale del processo consumatore.
|
|
|
|
*(vedi slides per gestore 'smistatore')*
|
|
#### Interfaccia produttore
|
|
|
|
```
|
|
produttore {
|
|
while true {
|
|
... produce dato d ...
|
|
send (d) to gestore.dati
|
|
}
|
|
}
|
|
```
|
|
#### Interfaccia consumatore
|
|
|
|
```
|
|
consumatore {
|
|
port T dati;
|
|
.
|
|
.
|
|
.
|
|
send (s) to gestore.pronto
|
|
p = receive (d) from dati
|
|
}
|
|
}
|
|
```
|
|
|
|
### Piu' produttori, piu' consumatori
|
|
|
|
Caso con **buffer limitato**:
|
|
|
|
* I consumatori sono analoghi al caso precedente
|
|
* I processi produttori sono multipli, quindi devono segnalare allo smistatore che sono pronti a inviare dati. Il buffer limitato fa si che il gestore debba tenere un contatore per controllare che i produttori possano aggiungere dati al buffer.
|
|
|
|
Il contatore del gestore viene aumentato quando i dati sono inviati dal produttore, e diminuito quando ci sono consumatori che richiedono dati. I consumatori sono quindi autorizzati a ricevere dati solo se il contatore e' `> 0`, mentre i produttori sono autorizzati a mandare dati solo se il contatore e' `< N` (dove N e' la dimensione del buffer).
|
|
|
|
**Questa e' un' applicazione delle guardie**.
|
|
|
|
*(vedi slides per gestore)*
|
|
|
|
#### Interfaccia produttore
|
|
|
|
```
|
|
produttore {
|
|
port S OK_send;
|
|
|
|
while true {
|
|
... produce dato d ...
|
|
send (s) to gestore.pronto
|
|
receive (s) from OK_send
|
|
send (d) to gestore.dati
|
|
}
|
|
}
|
|
```
|
|
### Problema delle strategie di priorita'
|
|
|
|
Suppongo un server che gestisca un pool di risorse, e client che fanno richieste per ottenere una risorsa e deve ottenerla quello che ha priorita' massima (gli altri si bloccano mentre sono in attesa).
|
|
|
|
*(vedi slides per codice server / esempio di guardie )*
|
|
|
|
Le priorita' non sono specificate: le uniche informazioni sono quelle relative alla priorita' dei processi (il primo in coda e' quello con priorita' massima, l'ultimo quello con priorita' minima). Le risorse sono equivalenti.
|
|
|
|
#### Interfaccia client
|
|
|
|
```
|
|
client {
|
|
port int risorsa
|
|
...
|
|
send (s) to server.richiesta
|
|
p = receive (r) from risorsa
|
|
... usa risorsa...
|
|
send (r) to server.rilascio
|
|
}
|
|
```
|
|
Il client ha un'interfaccia semplice perche' la priorita' e' tutta gestita dal server.
|
|
|
|
### Esercizi
|
|
|
|
* l'esercizio 2 e' un crivello di Eratostene (genera tutti i primi filtrando i multipli)
|
|
* l'esercizio 3 e' un rifacimento dell'esercizio H2O
|
|
|