# data[].attributes.body
Prima di affrontare la scrittura di un programma parallelo è sempre necessario soffermarsi ad analizzare il grafo delle precedenze per cercare di semplificarlo eliminando le precedenze implicite

In presenza di precedenze implicite è possibile togliere un arco in quanto questo risulta ridondante ed è quindi possibile semplificare il grafo

# data[].attributes.body
La codifica con il costrutto fork-join è invece fattibile, ma è necessario introdurre le istruzioni di salto in quanto le fork si intersecano tra loro:

# data[].attributes.headline
Equivalenza di fork-join e cobegin-coend

# data[].attributes.subject
Equivalenza di fork-join e cobegin-coend

# data[].attributes.body
Questo grafo non presenta questa caratteristica e quindi costituisce un esempio impossibile da descrivere soltanto con cobegin e coend

# data[].attributes.body
La sintassi del comando è

# data[].attributes.body
A volte viene anche messa una label per indicare dove il processo termina ed effettua la join, come nel seguente esempio

# data[].attributes.body
Codifichiamo il diagramma precedente

# data[].attributes.body
count è una variabile intera non negativa e indica il numero di processi che si “riuniscono” in quel punto

# data[].attributes.body
Esiste anche una formulazione estesa dell’istruzione join

# data[].attributes.body
Ipotizziamo che ogni nodo sia una routine:

P1 esegue la routine A;

con la fork attiva il P2 e in parallelo si eseguono la routine B e la routine C

P1 attende con la join la terminazione di P2

P1esegue la routine C

# data[].attributes.body
La join è l’istruzione che viene eseguita quando il processo creato tramite la fork:

ha terminato il suo compito

si sincronizza con il processo che lo ha generato termina la sua esecuzione

# data[].attributes.body
Con un linguaggio di pseudocodifica è possibile tradurre il grafo nel seguente segmento di codice:

# data[].attributes.body
Le istruzioni fork e join furono introdotte nel 1963 da Dennis e VanHorne. Permettono di descrivere l’esecuzione parallela di segmenti di codice mediante:

la scomposizione di un processo in due processi

la successiva “riunione” in un unico processo

# data[].attributes.body
Per scrivere programmi non sequenziali (o concorrenti) è necessario avere a disposizione dei particolari costrutti che permettano la descrizione delle attività parallele. Non tutti i linguaggi hanno tali figure strutturali. Quelli che consentono la descrizione delle attività concorrenti prendono il nome di linguaggi di programmazione concorrente (o non sequenziale).

La scrittura di un programma concorrente si basa sul concetto di scomposizione sequenziale (un processo non sequenziale può essere scomposto in un insieme di processi sequenziali che possono essere eseguiti contemporaneamente)

I linguaggi di programmazione concorrenti di alto livello contemplano nuove istruzioni come il costrutto fork-join e cobegin-coend.
Permettono di dichiarare, creare, attivare e terminare processi sequenziali.

# data[].attributes.body
L’elaboratore non sequenziale è una macchina che deve essere in grado di eseguire più operazioni contemporaneamente si può ottenere in due modi differenti:

architettura parallela, cioè sistemi multielaboratori

sistemi monoprocessori multiprogrammati

# data[].attributes.body
L’esecuzione di un processo non sequenziale richiede un sistema specifico che permetta la codifica e l’esecuzione dei programmi, cioè necessita di:

un elaboratore non sequenziale

un linguaggio di programmazione non sequenziale

# data[].attributes.body
- l'istruzione fork-join
- l'istruzione cobegin-coend

# data[].attributes.headline
LA DESCRIZIONE DELLA CONCORRENZA (Lezione 2)

# data[].attributes.subject
LA DESCRIZIONE DELLA CONCORRENZA (Lezione 2)