problemi con la concorrenza sincronizzazione di processi e...

12
Operating Systems and Distributed Systems Sincronizzazione di processi e thread (1) #define NITER 1000000 int count = 0; void *ThreadAdd(void *); /*thread’s function prototype */ int main(int argc, char * argv[]) { pthread_t tid1, tid2; pthread_create(&tid1, NULL, ThreadAdd, NULL); /* creates thread 1 */ pthread_create(&tid2, NULL, ThreadAdd, NULL); /* creates thread 2 */ pthread_join(tid1, NULL); /* wait for the thread 1 to finish */ pthread_join(tid2, NULL); /* wait for the thread 2 to finish */ if (count < 2 * NITER) printf("\n BOOM! count is [%d], should be %d\n", count, 2*NITER); else printf("\n OK! count is [%d]\n", count); pthread_exit(NULL); } void *ThreadAdd(void * a) /*thread’s function implementation */ { int i, tmp; for(i = 0; i < NITER; i++){ tmp = count; /* copy the global count locally */ tmp = tmp+1; /* increment the local copy */ count = tmp; /* store the local value into the global count */ } } Operating Systems and Distributed Systems 2 Problemi con la concorrenza host:pthreadsync Bebo$ ./p_thradd BOOM! il conteggio e! [1008904], invece di 2000000 host:pthreadsync Bebo$ ./p_thradd BOOM! il conteggio e! [1050931], invece di 2000000 host:pthreadsync Bebo$ ./p_thradd BOOM! il conteggio e! [1014420], invece di 2000000 host:pthreadsync Bebo$ ./p_thradd BOOM! il conteggio e! [1001119], invece di 2000000 host:pthreadsync Bebo$ ./p_thradd BOOM! il conteggio e! [1009707], invece di 2000000 Operating Systems and Distributed Systems 3 Compile & run p_thradd.c! Risultati aleatori? Problemi con la concorrenza Operating Systems and Distributed Systems Il problema della concorrenza 4 Sincronizzazione: necessaria in condizioni di concorrenza. Concorrenza: due o più processi/thread sono in esecuzione nello stesso intervallo di tempo e potenzialmente interagiscono l’uno con l’altro (e.g., condivisione di risorse). Race condition Critical section

Upload: vanhanh

Post on 15-Feb-2019

219 views

Category:

Documents


0 download

TRANSCRIPT

Operating Systems and Distributed Systems

Sincronizzazione di processi e thread (1)

#define NITER 1000000

int count = 0;

void *ThreadAdd(void *); /*thread’s function prototype */

int main(int argc, char * argv[])

{

pthread_t tid1, tid2;!

pthread_create(&tid1, NULL, ThreadAdd, NULL); /* creates thread 1 */!

pthread_create(&tid2, NULL, ThreadAdd, NULL); /* creates thread 2 */ !

pthread_join(tid1, NULL);! /* wait for the thread 1 to finish */ !

pthread_join(tid2, NULL); /* wait for the thread 2 to finish */

!

if (count < 2 * NITER) printf("\n BOOM! count is [%d], should be %d\n", count, 2*NITER);

else printf("\n OK! count is [%d]\n", count);

!

pthread_exit(NULL);

}

void *ThreadAdd(void * a) /*thread’s function implementation */

{

int i, tmp;

for(i = 0; i < NITER; i++){

tmp = count; /* copy the global count locally */

tmp = tmp+1; /* increment the local copy */

count = tmp; /* store the local value into the global count */

}

}

Operating Systems and Distributed Systems 2

Problemi con la concorrenza

host:pthreadsync Bebo$ ./p_thradd

BOOM! il conteggio e! [1008904], invece di 2000000

host:pthreadsync Bebo$ ./p_thradd

BOOM! il conteggio e! [1050931], invece di 2000000

host:pthreadsync Bebo$ ./p_thradd

BOOM! il conteggio e! [1014420], invece di 2000000

host:pthreadsync Bebo$ ./p_thradd

BOOM! il conteggio e! [1001119], invece di 2000000

host:pthreadsync Bebo$ ./p_thradd

BOOM! il conteggio e! [1009707], invece di 2000000

Operating Systems and Distributed Systems 3

Compile & run p_thradd.c!

Risultati

aleatori?

Problemi con la concorrenza

Operating Systems and Distributed Systems

Il problema della concorrenza

4

• Sincronizzazione: necessaria in condizioni di concorrenza.

• Concorrenza: due o più processi/thread sono in esecuzione nello stesso intervallo di tempo e potenzialmente interagiscono l’uno con l’altro (e.g., condivisione di risorse).

Race condition

Criticalsection

Operating Systems and Distributed Systems 5

• Race condition: La manipolazione simultanea della stessa risorsa da parte di due o più P/T causa risultati inconsistenti.

• Sezione critica (Critical section): Segmento di codice che coordina l’accesso a una risorsa condivisa.

• Mutua esclusione (Mutual exclusion): proprietà del software che assicura accesso esclusivo a una risorsa condivisa.

• Deadlock: condizione speciale di stallo creata da due o più P/T e due o più locks su risorse che bloccano il lavoro.

Concetti importanti

Thread 1 Thread 2 count

tmp = count; --- 0

tmp = tmp +1; --- 0

--- tmp = count;!!// 0 0

--- tmp = tmp + 1; 0

count = tmp;!!// 1 --- 1

--- count = tmp;!!// 1 1

Operating Systems and Distributed Systems 6

tmp = count;

tmp = tmp+1;

count = tmp;

Thread 1 Thread 2

tmp = count;

tmp = tmp+1;

count = tmp;

Race condition: esempio con pthread

Thread 1 Thread 2 count

tmp = count; --- 0

tmp = tmp +1; --- 0

--- tmp = count;!!// 0 0

--- tmp = tmp + 1; 0

count = tmp;!!// 1 --- 1

--- count = tmp;!!// 1 1

Operating Systems and Distributed Systems 7

tmp = count;

tmp = tmp+1;

count = tmp;

Thread 1 Thread 2

tmp = count;

tmp = tmp+1;

count = tmp;

Race condition: esempio con pthread

Thread 1 Thread 2 count

tmp = count; --- 0

tmp = tmp +1; --- 0

--- tmp = count;!!// 0 0

--- tmp = tmp + 1; 0

count = tmp;!!// 1 --- 1

--- count = tmp;!!// 1 1

Operating Systems and Distributed Systems 8

tmp = count;

tmp = tmp+1;

count = tmp;

Thread 1 Thread 2

tmp = count;

tmp = tmp+1;

count = tmp;

Race condition: esempio con pthread

Thread 1 Thread 2 count

tmp = count; --- 0

tmp = tmp +1; --- 0

--- tmp = count;!!// 0 0

--- tmp = tmp + 1; 0

count = tmp;!!// 1 --- 1

--- count = tmp;!!// 1 1

Operating Systems and Distributed Systems 9

tmp = count;

tmp = tmp+1;

count = tmp;

Thread 1 Thread 2

tmp = count;

tmp = tmp+1;

count = tmp;

Race condition: esempio con pthread

Thread 1 Thread 2 count

tmp = count; --- 0

tmp = tmp +1; --- 0

--- tmp = count;!!// 0 0

--- tmp = tmp + 1; 0

count = tmp;!!// 1 --- 1

--- count = tmp;!!// 1 1

Operating Systems and Distributed Systems 10

tmp = count;

tmp = tmp+1;

count = tmp;

Thread 1 Thread 2

tmp = count;

tmp = tmp+1;

count = tmp;

Race condition: esempio con pthread

Thread 1 Thread 2 count

tmp = count; --- 0

tmp = tmp +1; --- 0

--- tmp = count;!!// 0 0

--- tmp = tmp + 1; 0

count = tmp;!!// 1 --- 1

--- count = tmp;!!// 1 1

Operating Systems and Distributed Systems 11

tmp = count;

tmp = tmp+1;

count = tmp;

Thread 1 Thread 2

tmp = count;

tmp = tmp+1;

count = tmp;

Race condition: esempio con pthread

Operating Systems and Distributed Systems

Meccanismi di sincronizzazione

Operating Systems and Distributed Systems13

Problema: race condition

• I thread P1 e P2 scrivono nella memoria condivisa

• Problema: non è possibile garantire che la read seguita da una write sia atomica

– L’ordine di esecuzione ha importanza!

• Necessario eliminare le race condition…

R1 <= x

R1 = R1+1

R1 => x

R3 <= x

R3 = R3+1

R3 => x

P1 P2x=3

x=5R1 <= x

R1 = R1+1

R1 => x

R3 <= x

R3 = R3+1

R3 => x

x=6!

LOAD

LOAD

STORE

STORE

STORE

LOAD

LOAD

STORE

Operating Systems and Distributed Systems

Il problema del latte

Arriva a casa

Frigorifero: niente latte

Va a comprare in latteria

arriva in latteria

Compra latte

Arriva a casa: mette in frigo

Arriva a casa

Frigorifero: niente latte

Va a comprare in latteria

Compra latte

Arriva a casa: mette in frigo

Primo studente Secondo studenteOra

Operating Systems and Distributed Systems

• Si assume di avere delle load/store atomiche

• Lascia un biglietto (lock)

• Togli il biglietto (unlock)

• Non comprare latte se c’è un biglietto (wait)

Il problema del latte

Operating Systems and Distributed Systems

Il problema del latte: soluzione 1

troppo latte!Compile & run p_milknota.c!

Operating Systems and Distributed Systems

Il problema del latte: soluzione 2

niente latte! (starvation)

usiamo bigliettini etichettati

Compile & run p_milknota2.c!

Operating Systems and Distributed Systems

Il problema del latte: soluzione 3aspetta il biglietto giusto

Operating Systems and Distributed Systems

Il problema del latte: soluzione 3aspetta il biglietto giusto

prima A e poi B

Operating Systems and Distributed Systems

Il problema del latte: soluzione 3aspetta il biglietto giusto

prima B e poi A

Operating Systems and Distributed Systems

Il problema del latte: soluzione 3aspetta il biglietto giusto

interleaving: A aspetta e poi compra

Operating Systems and Distributed Systems

Il problema del latte: soluzione 3aspetta il biglietto giusto

interleaving: A aspetta, B compra

Compile & run p_milknota3.c!

• Si definiscono delle regioni critiche (RC) a cui si applica la mutual exclusion per governare la corsa

Operating Systems and Distributed Systems

non_critical_sectionENTER_REGIONcritical_sectionLEAVE_REGIONnon_critical_section

{ }

Protezione regioni critiche

Operating Systems and Distributed Systems24

Process A

Process B B blocked

A enterscritical region

B tries to entercritical region

B enterscritical region

A leavescritical region

B leavescritical region

Time

Protezione regioni critiche • Quattro condizioni da soddisfare

1. Non ci devono essere 2 processi simultaneamente in RC

2. Non si devono fare ipotesi su velocità e numero CPU

3. Nessun processo che esegue fuori dalla sua RC può bloccare un altro processo

4. Un processo non dovrebbe attendere indefinitamente per entrare nella sua RC

Operating Systems and Distributed Systems

Meccanismi di sincronizzazione: Locks

Operating Systems and Distributed Systems26

Locks a livello pthread: Mutex• Mutual exclusion lock:

– Blocca l’accesso a variabili da parte di threads:

• I Threads che vogliono l’accesso a risorse protette da un mutex devono attendere finchè il thread attivo finisca ed esegua l’ unlock del mutex.

• Usati per serializzare l’accesso a una risorsa condivisa..

• Possono essere applicati a threads in un single processo e non fra processi (devo usare semafori).

non_critical_sectionpthread_mutex_lock( &mutex1 )

critical_sectionpthread_mutex_unlock( &mutex1 )

non_critical_section

{ }

int counter=0;

/* Function C */

void functionC(){

! counter++!}

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;

int counter=0;

/* Function C */

void functionC()

{

! pthread_mutex_lock( &mutex1 );

! counter++

! pthread_mutex_unlock( &mutex1 );

}

Operating Systems and Distributed Systems27

senza Mutex con Mutex

Thread 1 Thread 2

counter = 0

counter = 1

counter = 0

counter = 1

Thread 1 Thread 2

counter = 0

counter = 1

counter = 0

counter = 2

Thread 2 locked out.Thread 1 ha uso esclusivo di counter

Locks a livello pthread: Mutex

Operating Systems and Distributed Systems 28

pthread_mutex_t myMutex; //dichiarazione

myMutex = PTHREAD_MUTEX_INITIALIZER; //inizializzazione

//statica

int pthread_mutex_init(pthread_mutex_t *mutex,

const pthread_mutex_attr_t *attr);

//inizializzazione dinamica

// attr=NULL se si usano attributi // di default

Locks a livello pthread: Mutex

Operating Systems and Distributed Systems 29

• lock / unlock

– pthread_mutex_lock: blocca la calling function fino a pthread_mutex_unlock.

– più di un thread waiting sullo stesso mutex: la pthread library usa la scheduling priority dei thread in attesa

int pthread_mutex_lock(pthread_mutex_t *mutex);

int pthread_mutex_unlock(pthread_mutex_t *mutex);

Locks a livello pthread: Mutex

• lock without blocking,

int pthread_mutex_trylock(pthread_mutex_t *mutex);

Operating Systems and Distributed Systems 30

• deallocate

int pthread_mutex_destroy(pthread_mutex_t *mutex);

Locks a livello pthread: Mutex

#define NITER 1000000

int count = 0;

pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; //initialize a mutex object statically

void * ThreadAdd(void *); //function executed by threads

int main(int argc, char * argv[]){

pthread_t tid1, tid2;

!

pthread_create(&tid1, NULL, ThreadAdd, NULL))!

pthread_create(&tid2, NULL, ThreadAdd, NULL))

pthread_join(tid1, NULL))! /* wait for the thread 1 to finish */

pthread_join(tid2, NULL)) /* wait for the thread 2 to finish */!

if (count < 2 * NITER) printf("\n BOOM! count is [%d], should be %d\n", count, 2*NITER);

else printf("\n OK! count is [%d]\n", count);!

! // Clean up the mutex

! pthread_mutex_destroy(&mutex1);

pthread_exit(NULL);

}

void * ThreadAdd(void * a){

int i, tmp;

for(i = 0; i < NITER; i++){

! pthread_mutex_lock( &mutex1 );

/*****************************************************************/

tmp = count; /* copy the global count locally */

tmp = tmp+1; /* increment the local copy */

count = tmp; /* store the local value into the global count */

/*****************************************************************/

! pthread_mutex_unlock( &mutex1 );

}

}

Operating Systems and Distributed Systems 31

Mutex p_thread: Esempio 1

host:Lez20_ex Bebo$ ./p_thraddmx

OK! count is [2000000]

host:Lez20_ex Bebo$ ./p_thraddmx

OK! count is [2000000]

host:Lez20_ex Bebo$ ./p_thraddmx

OK! count is [2000000]

host:Lez20_ex Bebo$

Operating Systems and Distributed Systems 32

Compile and run p_thraddmx.c!

Mutex p_thread: Esempio 1

pthread_mutex_t mymutex;

long counter; //shared data

void *thread_fun(void *);

int main()

{

! pthread_t t1;

! void *exit_status;

! int i;

! //Initialize the mutex before trying to use it.

! pthread_mutex_init(&mymutex, NULL);!

! pthread_create(&t1, NULL, thread_fun, NULL);!

! for (i = 0 ; i < 10 ; ++i ){ // Try to use the shared data.

! ! sleep(1) ;

! ! pthread_mutex_lock(&mymutex);

! ! printf( "\rShared integer's value = %ld\n", counter);

! ! pthread_mutex_unlock(&mymutex);! !

! }

! printf("\n");

! pthread_join(t1, &exit_status);

! pthread_mutex_destroy(&mymutex);

! printf( "\rShared integer's final value = %ld\n", counter);

! return 0;

}

void *thread_fun(void *arg){

! long i;!!

! for (i=0; i<10000*10000; i++){

! ! //accessing the shared data

! ! pthread_mutex_lock(&mymutex);

! ! counter++;

! ! pthread_mutex_unlock(&mymutex);

! }

!

! return NULL;

} Operating Systems and Distributed Systems 33

dynamicinitialization

Compile and run p_thraddmx2.c!

Mutex p_thread: Esempio 2

void* compratore(void *arg){ pthread_mutex_lock(&VadoComprareLatte);! /*************** Entra regione critica**************/! thdata *data; data = (thdata *) arg; /* type cast */!

! printf("Thread %d \n", data->thread_no);! printf("\nLatte disponibile ora %d\n", latteDisponibile); if(!latteDisponibile) { // compra ++latteDisponibile; }!

! /*************** Fine regione critica**************/ pthread_mutex_unlock(&VadoComprareLatte);!

return NULL;}

Operating Systems and Distributed Systems

Mutex p_thread: Il problema del latte

Compile & run p_milkmutex.c!

void *function2()

{

! ...

! pthread_mutex_lock(&lock2); - Execution step 2

! pthread_mutex_lock(&lock1);

! ...

! pthread_mutex_lock(&lock1);

! pthread_mutex_lock(&lock2);

! ...

}

Operating Systems and Distributed Systems 35

Attenzione ai DEADLOCK• L’ordine è importante: deadlock:

main()

{

! ...

! pthread_create(&thread1, NULL, function1, NULL);

! pthread_create(&thread2, NULL, function2, NULL);

! ...

}

void *function1()

{

! ...

! pthread_mutex_lock(&lock1); - Execution step 1

! pthread_mutex_lock(&lock2); - Execution step 3 DEADLOCK!!!

! ...

! pthread_mutex_lock(&lock2);

! pthread_mutex_lock(&lock1);

! ...

}

se function1 acquisisce il primo mutex and function2 acquisisce il secondo, tutte le risorse sono bloccate.

Operating Systems and Distributed Systems

Meccanismi di sincronizzazione:come implementare i lock

Operating Systems and Distributed Systems

Soluzione hardware (1):Disabilitazione delle interruzioni

int count; //condivisa

Codice p1 Codice p2

disableInterrupts(); disableInterrupts();

count++; count++;

enableInterrupts(); enableInterrupts();

• Va bene per il kernel

• Non adeguato per user process

• Inutile per architettura multiprocessor

2. Non si devono fare ipotesi su velocità e numero CPU

Operating Systems and Distributed Systems

• Interrupts potrebbero essere disabilitati per un tempo arbitrario

• Vogliamo solo che p1 e p2 non interferiscano;

così si bloccano tutti i pi

• Possibilità: usare una variabile condivisa di “lock”

Soluzione hardware (1):Disabilitazione delle interruzioni

int count; //condivisa

Codice p1 Codice p2

disableInterrupts(); disableInterrupts();

count++; count++;

enableInterrupts(); enableInterrupts();

Operating Systems and Distributed Systems

Condizione di Busy Waitboolean lock = FALSE; //condivisa

int count; //condivisa

Codice p1 Codice p2

/* Acquisire lock */ /* Acquisire lock */

while(lock) ; while(lock) ;

lock = TRUE; lock = TRUE;

/* Sezione critica */ /* Sezione critica */

count++; count++;

/* Rilasciare lock */ /* Rilasciare lock */

lock = FALSE; lock = FALSE;

p1

p2

Blo

cked

at while

lock = TRUE

lock = FALSE

Inte

rrupt

Inte

rrupt

Inte

rrupt

Operating Systems and Distributed Systems

Problemi....

• Di male in peggio … abbiamo una nuova race condition …

1. Non ci devono essere 2 processi simultaneamente in RC

boolean lock = FALSE; //condivisa

int count; //condivisa

Codice p1 Codice p2

/* Acquisire lock */ /* Acquisire lock */

while(lock) ; while(lock) ;

lock = TRUE; lock = TRUE;

/* Sezione critica */ /* Sezione critica */

count++; count++;

/* Rilasciare lock */ /* Rilasciare lock */

lock = FALSE; lock = FALSE;

Operating Systems and Distributed Systems

Gestione Atomica del Lock(1)

enter(lock) { exit(lock) {

disableInterrupts(); disableInterrupts();

/* Loop finchè lock è TRUE */ lock = FALSE;

while(lock) { enableInterrupts();

/* Garantisce gli interrupts */ }

enableInterrupts();

disableInterrupts();

}

lock = TRUE;

enableInterrupts();

}

• Limita il tempo di disabilitazione degli interrupts • Ancora costosa

Operating Systems and Distributed Systems

Meccanismi di sincronizzazione:test &set

Operating Systems and Distributed Systems

• Evitare l’ interrupt disabling

• CPU ha nell’ instruction set istruzioni atomiche per il one-shot testing and setting del lock

– OS/360: Test and Set Lock (TSL)

– x86: LOCK BTS

non_critical_sectionENTER_REGIONcritical_sectionLEAVE_REGIONnon_critical_section

{ }

Meccanismi di sincronizzazione:test &set

Operating Systems and Distributed Systems

• Una variabile di lock

• Ancora busy waiting, ma codice più semplice

• non c’e’ limite al numero di processi

– Ci potrebbe essere un waiting illimitato

3. Nessun processo che esegue fuori dalla sua RC può bloccare un altro processo

Meccanismi di sincronizzazione:test &set

Operating Systems and Distributed Systems45

Eliminare il busy waiting• Obbiettivo: risparmiare CPU time

• Problema: priority inversion:

– H = high priority process, L = low priority process

• L è in RC, H diventa ready to run

• H diventa running in busy waiting

• L mai schedulato quando H running: L non può terminare e uscire dalla RC

• H aspetta all’inifinito....

• Approccio sleep/wakeup

4. Un processo non dovrebbe attendere indefinitamente per entrare nella sua RC

Operating Systems and Distributed Systems46

Eliminare il busy waiting

Operating Systems and Distributed Systems47

Eliminare il busy waiting

Operating Systems and Distributed Systems

Meccanismi di sincronizzazione