I'm a Pthreads newbie and I've been giving myself a challenge: I want to have a resource that multiple threads can access at the same time as read. However, if a thread wants to write, then this need to be exclusive.Now, I know that there are read/write locks designed specifically for this purpose. I would not do this in production code; this is just to learn. For simplicity, I assume there is only one thread that can write (but of course, multiple that read).I have come up with two version. The first one does not use signals, the second one does. I tested it a little bit and both seem to work. However, multi-thread programming is tricky.Could someone review the two versions and let me know whether there might be a bug that my tests missed?Version 1#include <pthread.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string>class limited_resource{public:             limited_resource()                {                pthread_mutex_init(&d_mutext, NULL);                d_write_request = false;                d_nreading = 0;                }    virtual ~limited_resource()                {                pthread_mutex_destroy(&d_mutext);                }    void read(int thread_id)        {        // Do a stupid waiting        while(true)            {            pthread_mutex_lock(&d_mutext);            if(!d_write_request)                {                d_nreading++;                break;                }            pthread_mutex_unlock(&d_mutext);            sleep(1);            }        pthread_mutex_unlock(&d_mutext);        // Do the actual reading        char data[4];        for(int ii = 0; ii < 4; ++ii)            {            data[ii] = d_data[ii];            sleep(1); // Super slow reading to create bugs on purpose            }        printf(Thread %d read \%c%c%c%c\\n, thread_id, data[0], data[1], data[2], data[3]);        // Decrement the number of readers        pthread_mutex_lock(&d_mutext);        d_nreading--;        pthread_mutex_unlock(&d_mutext);        }    void write(const char *str)        {        pthread_mutex_lock(&d_mutext);        d_write_request = true;        pthread_mutex_unlock(&d_mutext);        // Do a stupid waiting        while(true)            {            pthread_mutex_lock(&d_mutext);            if(d_nreading == 0)                break;            pthread_mutex_unlock(&d_mutext);            sleep(1);            }        pthread_mutex_unlock(&d_mutext);        // Do the work        printf(Writing\n);        for(int ii = 0; ii < 4; ++ii)            {            d_data[ii] = str[ii];            sleep(1); // Super slow writing to create bugs on purpose            }        // Release the writing        pthread_mutex_lock(&d_mutext);        d_write_request = false;        pthread_mutex_unlock(&d_mutext);        }protected:    pthread_mutex_t d_mutext;    int d_nreading;    bool d_write_request;    char d_data[4];};limited_resource lr;void* thread_function(void* input){    int thread_id = *((int*)input);    while(true)        {        sleep(thread_id + 1);        lr.read(thread_id);        }    return NULL;}int main(int argc, char **argv){    int N = 10;    pthread_t tIDs[N];    int vals[N];    for(int ii = 0; ii < N; ++ii)        {        vals[ii] = ii;        pthread_create(tIDs + ii, NULL, thread_function, vals + ii);        }    char str[80];    while(true)        {        scanf(%s, str);        lr.write(str);        }    return 0;}Version 2#include <pthread.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string>class limited_resource{public:             limited_resource()                {                pthread_mutex_init(&d_mutext, NULL);                d_write_request = false;                pthread_cond_init(&d_write_request_false, NULL);                d_nreading = 0;                pthread_cond_init(&d_nreading_zero, NULL);                }    virtual ~limited_resource()                {                pthread_mutex_destroy(&d_mutext);                pthread_cond_destroy(&d_write_request_false);                pthread_cond_destroy(&d_nreading_zero);                }    void read(int thread_id)        {        // Wait        pthread_mutex_lock(&d_mutext);        while(d_write_request)            pthread_cond_wait(&d_write_request_false, &d_mutext);        d_nreading++;        pthread_mutex_unlock(&d_mutext);        // Do the actual reading        char data[4];        for(int ii = 0; ii < 4; ++ii)            {            data[ii] = d_data[ii];            sleep(1); // Super slow reading to create bugs on purpose            }        printf(Thread %d read \%c%c%c%c\\n, thread_id, data[0], data[1], data[2], data[3]);        // Decrement the number of readers        pthread_mutex_lock(&d_mutext);        d_nreading--;        if(d_nreading == 0)            pthread_cond_signal(&d_nreading_zero);        pthread_mutex_unlock(&d_mutext);        }    void write(const char *str)        {        pthread_mutex_lock(&d_mutext);        d_write_request = true;        while(d_nreading != 0)            pthread_cond_wait(&d_nreading_zero, &d_mutext);        pthread_mutex_unlock(&d_mutext);        // Do a stupid waiting        while(true)            {            pthread_mutex_lock(&d_mutext);            if(d_nreading == 0)                break;            pthread_mutex_unlock(&d_mutext);            sleep(1);            }        pthread_mutex_unlock(&d_mutext);        // Do the work        printf(Writing\n);        for(int ii = 0; ii < 4; ++ii)            {            d_data[ii] = str[ii];            sleep(1); // Super slow writing to create bugs on purpose            }        // Release the writing        pthread_mutex_lock(&d_mutext);        d_write_request = false;        pthread_cond_broadcast(&d_write_request_false);        pthread_mutex_unlock(&d_mutext);        }protected:    pthread_mutex_t d_mutext;    int d_nreading;    pthread_cond_t d_nreading_zero;    bool d_write_request;    pthread_cond_t d_write_request_false;    char d_data[4];};limited_resource lr;void* thread_function(void* input){    int thread_id = *((int*)input);    while(true)        {        sleep(thread_id + 1);        lr.read(thread_id);        }    return NULL;}int main(int argc, char **argv){    int N = 10;    pthread_t tIDs[N];    int vals[N];    for(int ii = 0; ii < N; ++ii)        {        vals[ii] = ii;        pthread_create(tIDs + ii, NULL, thread_function, vals + ii);        }    char str[80];    while(true)        {        scanf(%s, str);        lr.write(str);        }    return 0;}
Accessing limited resources with Pthreads
c++;multithreading;locking;pthreads
You forgot to remove the stupid waiting from version 2. Otherwise, it looks good. Not particularly efficient, but correct.You can and should remove this chunk of code from version 2. The code before it already does this:    // Do a stupid waiting    while(true)        {        pthread_mutex_lock(&d_mutext);        if(d_nreading == 0)            break;        pthread_mutex_unlock(&d_mutext);        sleep(1);        }    pthread_mutex_unlock(&d_mutext);Here's an optimization for you. Change:    if(d_nreading == 0)        pthread_cond_signal(&d_nreading_zero);to    if( (d_nreading == 0) && d_write_request)        pthread_cond_signal(&d_nreading_zero);Reader/writer locks are based on the assumption that reading is frequent and writing is rare. So the common case would be that there's no writing. Your code makes an extra call into the pthread library for each read unlock, even that call is not needed.