Friday, May 8, 2009

Implementando problema dos "Leitores e Escritores" utilizando semáforos.

Olá leitores! Continuamos aqui a série de posts sobre problemas de comunicação entre threads e processos, desta vez abordando o problema dos "Leitores e Escritores".
Esse clássico problema de concorrência modela o acesso a um banco de dados. A situação proposta é a de um banco de dados que é acessado por leitores e escritores. A operação de leitura não requer uso exclusivo de tal banco, isto é, outros leitores também podem estar presentes. Já a operação de escrita requer exclusividade de acesso. A questão então é: como sincronizar leitores e escritores?

A seguir, apresentamos uma solução para tal problema utilizando semáforos.
Finalmente, os testes apresentados comprovam o bom funcionamento de nossa implementação.

CÓDIGO-FONTE


#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <semaphore.h>
#include <errno.h>

#define NREADERS 5
#define NWRITERS 5
#define MAX_DB_TIME 2
#define MAX_TIME_BEFORE_START 4

sem_t *db;
pthread_mutex_t mutex;
int rc = 0;
char *readers[5] = {"Abraham", "Bill", "Carl", "David", "Ellen"};
char *writers[5] = {"Stri", "ManoPri", "Chipuruto", "Cone", "Flavio"};

int dummy[NREADERS];

void *reader(void *arg);
void *writer(void *arg);
void read_data_base();
void use_data_read();
void think_up_data();
void write_data_base();

void sem_close_n_unlink(sem_t *semId, char *semName);
void error_exit(const char*);
char *rand_str(char *mystr, int size);


int main() {

srand(time(NULL));

int res;
pthread_t reader_thread[NREADERS], writer_thread[NWRITERS];
void *thread_result;
char *dbsem_name; // utilizaremos o padrao POSIX named sem
int index;

//init dummy array
for (index = 0; index < NREADERS; index++){
dummy[index]=index;
}

// iniciar mutex
res = pthread_mutex_init(&mutex, NULL);
if(res != 0) error_exit("Inicializacao do mutex falhou");

// iniciar semaforos
// atencao: em sistemas darwin, utilizamos sem_open ao inves de sem_init
// no Mac OS X, apenas *semaforos nomeados* estao implementados.

db = sem_open(rand_str(dbsem_name,8),O_CREAT,0,1);
if (db == SEM_FAILED) printf("criacao de semaforo falhou.\n");

// criar threads
for (index = 0; index < NWRITERS; index ++) {
res = pthread_create(&writer_thread[index],NULL,writer, (void *)&dummy[index]);
if (res != 0) error_exit("Thread creation failed");
}
for (index = 0; index < NREADERS; index ++) {
res = pthread_create(&reader_thread[index],NULL,reader, (void *)&dummy[index]);
if (res != 0) error_exit("Criacao de thread falhou");
}

// join thread. é suficiente único join p/ este programa
// pois todas as threads tem loop infinito.

res = pthread_join(writer_thread[0],&thread_result);
if (res != 0) error_exit("Thread join falhou");

// kill semaphores, mutexes
pthread_mutex_destroy(&mutex);
// closing and unlink semaphores
sem_close_n_unlink(db, dbsem_name);

return 1;
}

void *reader(void *arg){
int id = *(int *) arg;

while (1) {
pthread_mutex_lock(&mutex);
printf("=%s will try to read.\n", readers[id]);
rc = rc + 1;
if (rc == 1) sem_wait(db); // se 1o leitor...
pthread_mutex_unlock(&mutex);
printf("==%s is reading, totaling %d readers.\n",
readers[id], rc);
read_data_base();
pthread_mutex_lock(&mutex);
rc = rc - 1;
if (rc == 0) {
sem_post(db); //se ultimo leitor...
printf("=%s is done reading and realease db.\n", readers[id]);
}
else {
printf("%s is done reading. %d readers are left\n",
readers[id], rc);
}
pthread_mutex_unlock(&mutex);
use_data_read();
}

pthread_exit("exiting thread.\n");
}

void *writer(void *arg){
int id = *(int *) arg;

while (1) {
think_up_data();
printf(".%s will try to write into db.\n", writers[id]);
sem_wait(db);
printf("..%s is writing into db.\n", writers[id]);
write_data_base();
sem_post(db);
printf(".%s is done writing and realesed db.\n", writers[id]);
}

pthread_exit("exiting thread.\n");
}

void read_data_base() {
sleep(rand() % MAX_DB_TIME);
}

void use_data_read() {
sleep(rand() % MAX_DB_TIME);
}

void think_up_data() {
sleep(rand() % MAX_DB_TIME);
}

void write_data_base() {
sleep(rand() % MAX_DB_TIME);
}

void error_exit(const char *msg) {
perror(msg);
exit(EXIT_FAILURE);
}

char *rand_str(char *mystr, int size) {
if ((mystr = calloc(size + 1, sizeof(char))) == NULL) {
printf("callor error.\n");
return NULL;
}

int aux;
for (aux = 0; aux < size ; aux++) {
mystr[aux] = (char)((rand() % 23) + 'a');
}
mystr[aux] = '\0';

return mystr;
}

void sem_close_n_unlink(sem_t *semId, char *semName) {

if (sem_close (semId) == -1) {
printf ("sem_close failed\n");
return;
}

if (sem_unlink (semName) == -1) {
printf ("sem_unlink failed\n");
return;
}
printf ("closed and unlinked semaphore\n");
}

TESTES

  

.Stri will try to write into db.
..Stri is writing into db.
.Stri is done writing and realesed db.
.Stri will try to write into db.
..Stri is writing into db.
.Chipuruto will try to write into db.
.Cone will try to write into db.
=Abraham will try to read.
.Stri is done writing and realesed db.
..Chipuruto is writing into db.
.Stri will try to write into db.
.ManoPri will try to write into db.
.Flavio will try to write into db.
.Chipuruto is done writing and realesed db.
..Cone is writing into db.
.Chipuruto will try to write into db.
.Cone is done writing and realesed db.
==Abraham is reading, totaling 1 readers.
=Bill will try to read.
.Cone will try to write into db.
==Bill is reading, totaling 2 readers.
=Carl will try to read.
==Carl is reading, totaling 3 readers.
=David will try to read.
==David is reading, totaling 4 readers.
=Ellen will try to read.
==Ellen is reading, totaling 5 readers.
Abraham is done reading. 4 readers are left
David is done reading. 3 readers are left
=Abraham will try to read.
==Abraham is reading, totaling 4 readers.
Bill is done reading. 3 readers are left
Ellen is done reading. 2 readers are left
Carl is done reading. 1 readers are left
=Carl will try to read.
==Carl is reading, totaling 2 readers.
=David will try to read.
==David is reading, totaling 3 readers.
Abraham is done reading. 2 readers are left
=Bill will try to read.
==Bill is reading, totaling 3 readers.
=Ellen will try to read.
==Ellen is reading, totaling 4 readers.
Carl is done reading. 3 readers are left
Ellen is done reading. 2 readers are left
=Ellen will try to read.
==Ellen is reading, totaling 3 readers.
David is done reading. 2 readers are left
=David will try to read.
==David is reading, totaling 3 readers.
=Abraham will try to read.
==Abraham is reading, totaling 4 readers.
Abraham is done reading. 3 readers are left
Bill is done reading. 2 readers are left
=Carl will try to read.
==Carl is reading, totaling 3 readers.
Carl is done reading. 2 readers are left
Ellen is done reading. 1 readers are left
=David is done reading and realease db.
=David will try to read.
..Stri is writing into db.
.Stri is done writing and realesed db.
..ManoPri is writing into db.
.Stri will try to write into db.
.ManoPri is done writing and realesed db.
.ManoPri will try to write into db.
..Flavio is writing into db.
.Flavio is done writing and realesed db.
..Chipuruto is writing into db.
.Flavio will try to write into db.

No comments:

Post a Comment