// Producer-consumer problem. // (c) 2008 Pedro Fortuny Ayuso. // This program gets three parameters (threshold, producers, consumers) // and creates 'producers' number of producers, 'consumers' number of // consumers and all of them keep doing their job. Consumers are unable // to 'eat' if a counter is 0 and producers are unable to produce if // the same counter is == threshold. A consumer decreases the counter // and a producer increases it [when they do their job]. // The status of the system is visualized with a series of dots and the // position of the cursor (your terminal may not #include #include #include #include #include #include // I tend to think this structure is more useful than naming // conventions. This way you associate an intrinsic mutex to // any variable, uniquely. typedef struct { pthread_mutex_t mutex; int value; } mutex_int; // OK, these are globals, could me made non-globals, but this is // just an exercise. int threshold; pthread_t full_mutex, empty_mutex; pthread_cond_t full, empty, cond; void *producer(void *count){ int sl = 0; mutex_int *c; c = (mutex_int *)count; for(;;){ if(pthread_mutex_lock(&c->mutex) != 0){ perror("Unable to lock a mutex"); pthread_exit(NULL); } // producers wait for a "not full" message from either // the main loop or the consumers while(c->value == threshold) if(pthread_cond_wait(&full, &c->mutex) != 0){ perror("Unable to wait upon condition"); pthread_exit(NULL); } sl = ++c->value; printf("."); // use this line if your terminal does not move the cursor //printf("[%i]\n", sl); fflush(stdout); // after producing, this producer broadcasts a "not empty" // message to all consumers; if((pthread_cond_broadcast(&empty) != 0) || (pthread_mutex_unlock(&c->mutex) != 0)){ perror("Unable to broadcast or unlock"); pthread_exit(NULL); } usleep(log(sl+1) * random()/3000 + 1); } } void *consumer(void *count){ int sl = 0; mutex_int *c; c = (mutex_int *)count; for(;;){ if(pthread_mutex_lock(&c->mutex) != 0){ perror("Unable to lock a mutex"); pthread_exit(NULL); } // consumers wait for a "not empty" message from producers // Notice that we MUST check the value every time we are // notified while(c->value == 0) if(pthread_cond_wait(&empty, &c->mutex) != 0){ perror("Unable to wait upon condition"); pthread_exit(NULL); } sl = --c->value; printf("\b"); // use this line if your terminal does not move the cursor //printf("[%i]\n", sl); fflush(stdout); // consumer broadcast a "not full" message to producers if((pthread_cond_broadcast(&full) != 0) || (pthread_mutex_unlock(&c->mutex) != 0)){ perror("Unable to broadcast or to unlock"); pthread_exit(NULL); } usleep(log((threshold - sl + 1)) * random()/3000); } } int main(int argc, char *argv[]) { //sranddev(); if(argc < 4){ printf("Usage: ./producer_consumer threshold producers consumers\n"); exit(1); } threshold = atoi(argv[1]); int producers = atoi(argv[2]); int consumers = atoi(argv[3]); if(threshold == 0 || producers <= 0 || consumers <= 0){ printf("Please, threshold and producers and cosumers must be positive\n"); exit(1); } if((pthread_cond_init(&full, NULL) != 0) || (pthread_cond_init(&empty, NULL) !=0)){ perror("Unable to init conditions"); exit(1); } mutex_int *count = (mutex_int *)calloc(1,sizeof(mutex_int)); if(count == NULL) { perror("Unable to malloc"); exit(1); } count->value = 0; if(pthread_mutex_init(&count->mutex, NULL) != 0){ perror("Unable to initiate mutex"); exit(1); } int i; for(i = 0; i< producers; i++){ pthread_t t; if(pthread_create(&t, NULL, producer, (void *) count) != 0){ perror("Unable to create thread"); exit(1); } } for(i = 0; i< consumers; i++){ pthread_t t; if(pthread_create(&t, NULL, consumer, (void *) count) != 0){ perror("Unable to create thread"); exit(1); } } if(pthread_cond_broadcast(&full) != 0){ perror("Unable to broadcast"); exit(1); }; pthread_exit(NULL); }