Diferenças
Aqui você vê as diferenças entre duas revisões dessa página.
Ambos lados da revisão anterior Revisão anterior Próxima revisão | Revisão anterior | ||
so:exclusao_mutua [2014/05/30 16:27] – [Alternância] maziero | so:exclusao_mutua [2022/11/30 14:38] (atual) – maziero | ||
---|---|---|---|
Linha 1: | Linha 1: | ||
+ | ====== O Problema da Exclusão Mútua ====== | ||
+ | |||
+ | Construir mecanismos para garantir a exclusão mútua entre processos ou //threads// não é uma tarefa trivial. Esta página traz alguns exemplos de mecanismos para obter a exclusão mútua - alguns dos quais não funcionam! | ||
+ | |||
+ | O código de base consiste em 100 // | ||
+ | |||
+ | Para medir a duração da execução, use o seguinte comando: | ||
+ | |||
+ | $ time < | ||
+ | |||
+ | ==== Sem coordenação ==== | ||
+ | |||
+ | Neste código, as threads acessam a variável compartilhada sem nenhum controle de concorrência. | ||
+ | |||
+ | <code c me1-none.c> | ||
+ | /* | ||
+ | Acesso concorrente a uma variável por muitas threads, sem controle. | ||
+ | |||
+ | Compilar com gcc -Wall me1-none.c -o me1 -lpthread | ||
+ | |||
+ | Carlos Maziero, DINF/UFPR 2020 | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define NUM_THREADS | ||
+ | #define NUM_STEPS 100000 | ||
+ | |||
+ | int sum = 0 ; | ||
+ | |||
+ | void *threadBody (void *id) | ||
+ | { | ||
+ | int i ; | ||
+ | |||
+ | for (i=0; i< NUM_STEPS; i++) | ||
+ | { | ||
+ | sum += 1 ; // critical section | ||
+ | } | ||
+ | |||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | |||
+ | int main (int argc, char *argv[]) | ||
+ | { | ||
+ | pthread_t thread [NUM_THREADS] ; | ||
+ | pthread_attr_t attr ; | ||
+ | long i, status ; | ||
+ | |||
+ | // define attribute for joinable threads | ||
+ | pthread_attr_init (&attr) ; | ||
+ | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) ; | ||
+ | |||
+ | // create threads | ||
+ | for(i=0; i< | ||
+ | { | ||
+ | status = pthread_create (& | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // wait all threads to finish | ||
+ | for (i=0; i< | ||
+ | { | ||
+ | status = pthread_join (thread[i], NULL) ; | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf ("Sum should be %d and is %d\n", NUM_THREADS*NUM_STEPS, | ||
+ | |||
+ | pthread_attr_destroy (&attr) ; | ||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== A solução ingênua ==== | ||
+ | |||
+ | Esta solução consiste em definir uma variável de controle '' | ||
+ | |||
+ | <code c me2-naive.c> | ||
+ | /* | ||
+ | Acesso concorrente a uma variável por muitas threads, solução " | ||
+ | |||
+ | Compilar com gcc -Wall me2-naive.c -o me2 -lpthread | ||
+ | |||
+ | Carlos Maziero, DINF/UFPR 2020 | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define NUM_THREADS | ||
+ | #define NUM_STEPS 100000 | ||
+ | |||
+ | int sum = 0 ; | ||
+ | int busy = 0 ; | ||
+ | |||
+ | // enter critical section | ||
+ | void enter_cs () | ||
+ | { | ||
+ | while ( busy ) ; // busy waiting | ||
+ | busy = 1 ; | ||
+ | } | ||
+ | |||
+ | // leave critical section | ||
+ | void leave_cs () | ||
+ | { | ||
+ | busy = 0 ; | ||
+ | } | ||
+ | |||
+ | void *threadBody(void *id) | ||
+ | { | ||
+ | int i ; | ||
+ | |||
+ | for (i=0; i< NUM_STEPS; i++) | ||
+ | { | ||
+ | enter_cs () ; | ||
+ | sum += 1 ; // critical section | ||
+ | leave_cs () ; | ||
+ | } | ||
+ | |||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | |||
+ | int main (int argc, char *argv[]) | ||
+ | { | ||
+ | pthread_t thread [NUM_THREADS] ; | ||
+ | pthread_attr_t attr ; | ||
+ | long i, status ; | ||
+ | |||
+ | // define attribute for joinable threads | ||
+ | pthread_attr_init (&attr) ; | ||
+ | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) ; | ||
+ | |||
+ | // create threads | ||
+ | for(i=0; i< | ||
+ | { | ||
+ | status = pthread_create (& | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // wait all threads to finish | ||
+ | for (i=0; i< | ||
+ | { | ||
+ | status = pthread_join (thread[i], NULL) ; | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf ("Sum should be %d and is %d\n", NUM_THREADS*NUM_STEPS, | ||
+ | |||
+ | pthread_attr_destroy (&attr) ; | ||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Alternância ==== | ||
+ | |||
+ | Esta solução consiste em definir uma variável '' | ||
+ | |||
+ | Esta solução funciona, mas é muuuuito lenta. Por que? | ||
+ | |||
+ | <code c me3-altern.c> | ||
+ | /* | ||
+ | Acesso concorrente a uma variável por muitas threads, solução com alternância. | ||
+ | |||
+ | Compilar com gcc -Wall me3-altern.c -o me3 -lpthread | ||
+ | |||
+ | Carlos Maziero, DINF/UFPR 2020 | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define NUM_THREADS | ||
+ | #define NUM_STEPS 100000 | ||
+ | |||
+ | int sum = 0 ; | ||
+ | int turn = 0 ; | ||
+ | |||
+ | // enter critical section | ||
+ | void enter_cs (long int id) | ||
+ | { | ||
+ | while (turn != id) ; // busy waiting | ||
+ | | ||
+ | if (sum % 100 == 0) | ||
+ | printf (" | ||
+ | } | ||
+ | |||
+ | // leave critical section | ||
+ | void leave_cs () | ||
+ | { | ||
+ | turn = (turn + 1) % NUM_THREADS ; | ||
+ | } | ||
+ | |||
+ | void *threadBody(void *id) | ||
+ | { | ||
+ | int i ; | ||
+ | |||
+ | for (i=0; i< NUM_STEPS; i++) | ||
+ | { | ||
+ | enter_cs ((long int) id) ; | ||
+ | sum += 1 ; // critical section | ||
+ | leave_cs () ; | ||
+ | } | ||
+ | |||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | |||
+ | int main (int argc, char *argv[]) | ||
+ | { | ||
+ | pthread_t thread [NUM_THREADS] ; | ||
+ | pthread_attr_t attr ; | ||
+ | long i, status ; | ||
+ | |||
+ | // define attribute for joinable threads | ||
+ | pthread_attr_init (&attr) ; | ||
+ | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) ; | ||
+ | |||
+ | // create threads | ||
+ | for(i=0; i< | ||
+ | { | ||
+ | status = pthread_create (& | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // wait all threads to finish | ||
+ | for (i=0; i< | ||
+ | { | ||
+ | status = pthread_join (thread[i], NULL) ; | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf ("Sum should be %d and is %d\n", NUM_THREADS*NUM_STEPS, | ||
+ | |||
+ | pthread_attr_destroy (&attr) ; | ||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Operações atômicas ==== | ||
+ | |||
+ | Esta solução consiste em usar operações atômicas, como // | ||
+ | |||
+ | <code c me4-tsl.c> | ||
+ | /* | ||
+ | Acesso concorrente a uma variável por muitas threads, solução com TSL. | ||
+ | |||
+ | Compilar com gcc -Wall me4-tsl.c -o me4 -lpthread | ||
+ | |||
+ | Carlos Maziero, DINF/UFPR 2020 | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define NUM_THREADS | ||
+ | #define NUM_STEPS 100000 | ||
+ | |||
+ | int sum = 0 ; | ||
+ | int lock = 0 ; | ||
+ | |||
+ | // enter critical section | ||
+ | void enter_cs (int *lock) | ||
+ | { | ||
+ | // atomic OR (Intel macro for GCC) | ||
+ | while (__sync_fetch_and_or (lock, 1)) ; // busy waiting | ||
+ | } | ||
+ | |||
+ | // leave critical section | ||
+ | void leave_cs (int *lock) | ||
+ | { | ||
+ | (*lock) = 0 ; | ||
+ | } | ||
+ | |||
+ | void *threadBody(void *id) | ||
+ | { | ||
+ | int i ; | ||
+ | |||
+ | for (i=0; i< NUM_STEPS; i++) | ||
+ | { | ||
+ | enter_cs (&lock) ; | ||
+ | sum += 1 ; // critical section | ||
+ | leave_cs (&lock) ; | ||
+ | } | ||
+ | |||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | |||
+ | int main (int argc, char *argv[]) | ||
+ | { | ||
+ | pthread_t thread [NUM_THREADS] ; | ||
+ | pthread_attr_t attr ; | ||
+ | long i, status ; | ||
+ | |||
+ | // define attribute for joinable threads | ||
+ | pthread_attr_init (&attr) ; | ||
+ | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) ; | ||
+ | |||
+ | // create threads | ||
+ | for(i=0; i< | ||
+ | { | ||
+ | status = pthread_create (& | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // wait all threads to finish | ||
+ | for (i=0; i< | ||
+ | { | ||
+ | status = pthread_join (thread[i], NULL) ; | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf ("Sum should be %d and is %d\n", NUM_THREADS*NUM_STEPS, | ||
+ | |||
+ | pthread_attr_destroy (&attr) ; | ||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== A Instrução XCHG ==== | ||
+ | |||
+ | Esta solução é similar à anterior, mas usa a instrução [[http:// | ||
+ | |||
+ | <code c me5-xchg.c> | ||
+ | /* | ||
+ | Acesso concorrente a uma variável por muitas threads, solução com instrução XCHG. | ||
+ | |||
+ | Compilar com gcc -Wall me5-xchg.c -o me5 -lpthread | ||
+ | |||
+ | Carlos Maziero, DINF/UFPR 2020 | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define NUM_THREADS | ||
+ | #define NUM_STEPS 100000 | ||
+ | |||
+ | int sum = 0 ; | ||
+ | int lock = 0 ; | ||
+ | |||
+ | // enter critical section | ||
+ | void enter_cs (int *lock) | ||
+ | { | ||
+ | int key = 1 ; | ||
+ | while (key) // busy waiting | ||
+ | { | ||
+ | // XCHG lock, key | ||
+ | __asm__ __volatile__(" | ||
+ | : " | ||
+ | : " | ||
+ | : " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // leave critical section | ||
+ | void leave_cs (int *lock) | ||
+ | { | ||
+ | (*lock) = 0 ; | ||
+ | } | ||
+ | |||
+ | void *threadBody(void *id) | ||
+ | { | ||
+ | int i ; | ||
+ | |||
+ | for (i=0; i< NUM_STEPS; i++) | ||
+ | { | ||
+ | enter_cs (&lock) ; | ||
+ | sum += 1 ; // critical section | ||
+ | leave_cs (&lock) ; | ||
+ | } | ||
+ | |||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | |||
+ | int main (int argc, char *argv[]) | ||
+ | { | ||
+ | pthread_t thread [NUM_THREADS] ; | ||
+ | pthread_attr_t attr ; | ||
+ | long i, status ; | ||
+ | |||
+ | // define attribute for joinable threads | ||
+ | pthread_attr_init (&attr) ; | ||
+ | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) ; | ||
+ | |||
+ | // create threads | ||
+ | for(i=0; i< | ||
+ | { | ||
+ | status = pthread_create (& | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // wait all threads to finish | ||
+ | for (i=0; i< | ||
+ | { | ||
+ | status = pthread_join (thread[i], NULL) ; | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf ("Sum should be %d and is %d\n", NUM_THREADS*NUM_STEPS, | ||
+ | | ||
+ | pthread_attr_destroy (&attr) ; | ||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Com Semáforos ==== | ||
+ | |||
+ | Esta solução controla a entrada na seção crítica através de um semáforo genérico POSIX. | ||
+ | |||
+ | <code c me6-semaphore.c> | ||
+ | /* | ||
+ | Acesso concorrente a uma variável por muitas threads, solução com semáforo. | ||
+ | |||
+ | Compilar com gcc -Wall me6-semaphore.c -o me6 -lpthread | ||
+ | |||
+ | Carlos Maziero, DINF/UFPR 2020 | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define NUM_THREADS | ||
+ | #define NUM_STEPS 100000 | ||
+ | |||
+ | int sum = 0 ; | ||
+ | sem_t s ; | ||
+ | |||
+ | void *threadBody(void *id) | ||
+ | { | ||
+ | int i ; | ||
+ | |||
+ | for (i=0; i< NUM_STEPS; i++) | ||
+ | { | ||
+ | sem_wait (&s) ; | ||
+ | sum += 1 ; // critical section | ||
+ | sem_post (&s) ; | ||
+ | } | ||
+ | |||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | |||
+ | int main (int argc, char *argv[]) | ||
+ | { | ||
+ | pthread_t thread [NUM_THREADS] ; | ||
+ | pthread_attr_t attr ; | ||
+ | long i, status ; | ||
+ | |||
+ | // initialize semaphore to 1 | ||
+ | sem_init (&s, 0, 1) ; | ||
+ | |||
+ | // define attribute for joinable threads | ||
+ | pthread_attr_init (&attr) ; | ||
+ | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) ; | ||
+ | |||
+ | // create threads | ||
+ | for(i=0; i< | ||
+ | { | ||
+ | status = pthread_create (& | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // wait all threads to finish | ||
+ | for (i=0; i< | ||
+ | { | ||
+ | status = pthread_join (thread[i], NULL) ; | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf ("Sum should be %d and is %d\n", NUM_THREADS*NUM_STEPS, | ||
+ | |||
+ | pthread_attr_destroy (&attr) ; | ||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Com Mutex ==== | ||
+ | |||
+ | Esta solução controla a entrada na seção crítica através de um mutex POSIX. | ||
+ | |||
+ | <code c me7-mutex.c> | ||
+ | /* | ||
+ | Acesso concorrente a uma variável por muitas threads, solução com mutex. | ||
+ | |||
+ | Compilar com gcc -Wall me7-mutex.c -o me7 -lpthread | ||
+ | |||
+ | Carlos Maziero, DINF/UFPR 2020 | ||
+ | */ | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | #include < | ||
+ | |||
+ | #define NUM_THREADS | ||
+ | #define NUM_STEPS 100000 | ||
+ | |||
+ | int sum = 0 ; | ||
+ | pthread_mutex_t mutex ; | ||
+ | |||
+ | void *threadBody(void *id) | ||
+ | { | ||
+ | int i ; | ||
+ | |||
+ | for (i=0; i< NUM_STEPS; i++) | ||
+ | { | ||
+ | pthread_mutex_lock (& | ||
+ | sum += 1 ; // critical section | ||
+ | pthread_mutex_unlock (& | ||
+ | } | ||
+ | |||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | |||
+ | int main (int argc, char *argv[]) | ||
+ | { | ||
+ | pthread_t thread [NUM_THREADS] ; | ||
+ | pthread_attr_t attr ; | ||
+ | long i, status ; | ||
+ | |||
+ | // initialize mutex | ||
+ | pthread_mutex_init (& | ||
+ | |||
+ | // define attribute for joinable threads | ||
+ | pthread_attr_init (&attr) ; | ||
+ | pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE) ; | ||
+ | |||
+ | // create threads | ||
+ | for(i=0; i< | ||
+ | { | ||
+ | status = pthread_create (& | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | // wait all threads to finish | ||
+ | for (i=0; i< | ||
+ | { | ||
+ | status = pthread_join (thread[i], NULL) ; | ||
+ | if (status) | ||
+ | { | ||
+ | perror (" | ||
+ | exit (1) ; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | printf ("Sum should be %d and is %d\n", NUM_THREADS*NUM_STEPS, | ||
+ | |||
+ | pthread_attr_destroy (&attr) ; | ||
+ | pthread_exit (NULL) ; | ||
+ | } | ||
+ | </ | ||