0

I would like to ask for help with this task.

My task is write an easy program in C which simulates readers-writers problem. The program requirements are:

  1. After program start, the user will be asked for enter count of writers and readers.

  2. Program will continously inform the user about status of the threads.

  3. After end of the program, little statistic (how many times had every reader read and every writer write).

I have done some basic structure of the program (dealing with the critical section through semaphores etc.), but I think that the program does not do, what it should do. Program runs without fault or unexpected behavior, but the counts, how many times had every reader read and every writer write are always one for each reader or writer).

Where am i wrong? Maybe I don't understand the task well.

Thank You very much for response.

Program code:

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

sem_t w;    // write access
sem_t m;    // mutex
int rc=0;   // readers count

int writersCount;
int readersCount;
pthread_t writersThread[10], readersThread[10];
int writeCount[10], readCount[10];
int i;

void *writer(void *i) {
    int a = *((int *) i);

    sem_wait(&w);   // P(w)
    printf("Writer %d writes to DB.\n",a+1);
    writeCount[a+1]++;  
    sem_post(&w);   // V(w)

    free(i);
}

void *reader(void *i) {
    int a = *((int *) i);

    sem_wait(&m);   // P(m)
    rc++;
    if (rc == 1) {
        sem_wait(&w);   // P(w)
    }
    sem_post(&m);   // V (m)

    printf("Reader %d reads from DB.\n",a+1);
    readCount[a+1]++;   

    sem_wait(&m);   // P(m)
    rc--;
    if (rc == 0) {
        sem_post(&w);   // V(w)
    }
    sem_post(&m);   // V(m)

    free(i);
}

int main() {
    sem_init(&w,0,1);
    sem_init(&m,0,1);

    printf("Enter count of writers:");
    scanf("%d",&writersCount);
    printf("Enter count of readers:");
    scanf("%d",&readersCount);

    for (i=0; i<readersCount; i++) {
        int *arg = malloc(sizeof(*arg));
        *arg = i;
        pthread_create(&readersThread[i], NULL, reader, arg);
    }
    for (i=0; i<writersCount; i++) {
        int *arg = malloc(sizeof(*arg));
        *arg = i;
        pthread_create(&writersThread[i], NULL, writer, arg);
    }
    for (i=0; i<writersCount; i++) {
        pthread_join(writersThread[i], NULL);
    }   
    for (i=0; i<readersCount; i++) {
        pthread_join(readersThread[i], NULL);
    }

    printf("--------------\n");
    for (i=0; i<readersCount; i++) {
        printf("Reader %d read %d times\n",i+1,readCount[i+1]);
    }

    for (i=0; i<writersCount; i++) {
        printf("Writer %d wrote %d times\n",i+1,writeCount[i+1]);
    }

    sem_destroy(&w);
    sem_destroy(&m);
    return 0;
}

Output: Enter count of writers:4 Enter count of readers:4

Reader 1 reads from DB.
Reader 3 reads from DB.
Reader 4 reads from DB.
Reader 2 reads from DB.
Writer 1 writes to DB.
Writer 2 writes to DB.
Writer 3 writes to DB.
Writer 4 writes to DB.
--------------
Reader 1 read 1 times
Reader 2 read 1 times
Reader 3 read 1 times
Reader 4 read 1 times
Writer 1 wrote 1 times
Writer 2 wrote 1 times
Writer 3 wrote 1 times
Writer 4 wrote 1 times
vasicbre
  • 96
  • 8
Pavel Straka
  • 427
  • 7
  • 19
  • 4
    You can't use global variables across threads. – Iharob Al Asimi Jan 13 '15 at 21:46
  • Thank You for response. I guess, you are talking about that variables int writeCount[10] and int readCount[10], am I right? Could You please suggest me proper way to count how many times, had reader read and writer write? Thank You. – Pavel Straka Jan 13 '15 at 21:57
  • Create a structure and pass it to `pthread_create` function, each thread it's own data. – Iharob Al Asimi Jan 13 '15 at 21:58
  • @iharob it seems to me that each thread is accesing it's own element in the arrays writeCount and readCount. There is only one shared variable rc that is protected with mutex m. – vasicbre Jan 13 '15 at 22:02
  • @vasicbre yes you are absolutely right. – Iharob Al Asimi Jan 13 '15 at 22:04
  • @PavelStarka why does each thread access (i+1)-th element of the array, why not i-th? – vasicbre Jan 13 '15 at 22:04
  • @iharob passing address of *i* is not a good idea since *i* is changed in the loop – vasicbre Jan 13 '15 at 22:07
  • @iharob Yes, you can use global variables in different threads. But you better make real sure that you have proper locking/synchronization in place before you do that... – twalberg Jan 13 '15 at 22:07
  • @vasicbre immediatly convinced me that you are right again, but the program was working as expected that way. The count was consistent with the read write reports, why could that be? – Iharob Al Asimi Jan 13 '15 at 22:10
  • @vasicbre , because i wanted to readers and writers to be numbered from 1 not from 0, but this is not so important. – Pavel Straka Jan 13 '15 at 22:12
  • I answered this problem few days back. http://stackoverflow.com/a/27869471/1646996 – qqibrow Jan 13 '15 at 22:38

2 Answers2

2

Your output is perfectly correct since every thread you run does exactly one read/write. Put some loops in reader/writer functions to change that fact. Your program may encounter an error when you run it with 10 readers or writers because one of them will try to access writeCount[10] or readCount[10], change that and you have a correct program.

vasicbre
  • 96
  • 8
  • 1
    And that's exactly why I had a different output, because passing the address of `i` was updating different elments in the counting arrays, because `i` was changing. I just realized it, but you posted the answer. +1 – Iharob Al Asimi Jan 13 '15 at 22:16
  • Thank You for response. But could You please be more specific about the loops? And what should I do with the global variables? Can i use them? I am confused from the reactions above. I am not familiar with C, so I don't know much even about this basic things. – Pavel Straka Jan 13 '15 at 22:18
  • 1
    Yes use the global variables, you are accessing them correctly with mutexes, and the loops could be for example random iterations within a reasonable range where you create a thread for the **same** reader/writer. – Iharob Al Asimi Jan 13 '15 at 22:20
  • 1
    @PavelStarka global variables are fine when using them securely as you are. Make one writer loop and do the writing several times. For example: `void *writer(void *i) { int a = *((int *) i); int rnd = rand() / RAND_MAX * 5.f; for(int j = 0; i < rnd; i++) { sem_wait(&w); // P(w) printf("Writer %d writes to DB.\n",a+1); writeCount[a+1]++; sem_post(&w); // V(w) } free(i); }` – vasicbre Jan 13 '15 at 22:22
  • @PavelStarka in that way your writers will "do the writing" between 0 and 5 times and your output won't be the same every time. Hope you understood why you were getting one as output every time in the first place. – vasicbre Jan 13 '15 at 22:27
  • @PavelStraka be careful with the thread index in your loops when you implement this idea. – Iharob Al Asimi Jan 13 '15 at 22:30
  • @PavelStarka don't use rand as I've written in the comment above, you'll get the zero every time because I didn't cast result of rand() to double in the hurry and now is too late to edit. I have also misstyped the loop counter, but you should get the general idea. – vasicbre Jan 13 '15 at 22:30
1

You can try this, but please pick vasicbre's answer because he is the one who found the problem first.

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>

sem_t w;    // write access
sem_t m;    // mutex
int rc=0;   // readers count

int writersCount;
int readersCount;
pthread_t writersThread[100], readersThread[100];
int writeCount[10], readCount[10];

void *writer(void *i)
{
    int a = *((int *) i);

    sem_wait(&w);   // P(w)
    printf("Writer %d writes to DB.\n",a+1);
    writeCount[a + 1]++;
    sem_post(&w);   // V(w)

    return NULL;
}

void *reader(void *i)
{
    int a = *((int *) i);

    sem_wait(&m);   // P(m)
    rc++;
    if (rc == 1) {
        sem_wait(&w);   // P(w)
    }
    sem_post(&m);   // V (m)

    printf("Reader %d reads from DB.\n", a + 1);
    readCount[a + 1]++;

    sem_wait(&m);   // P(m)
    rc--;
    if (rc == 0) {
        sem_post(&w);   // V(w)
    }
    sem_post(&m);   // V(m)

    return NULL;
}

int randomCount()
{
    return 5.0 * rand() / RAND_MAX;
}

int main()
{
    sem_init(&w,0,1);
    sem_init(&m,0,1);

    int i;

    printf("Enter count of writers:");
    scanf("%d",&writersCount);
    printf("Enter count of readers:");
    scanf("%d",&readersCount);

    int readerIndices[readersCount];
    int writerIndices[writersCount];
    int totalReaders = 0;
    int totalWriters = 0;

    for (i=0; i<readersCount; i++)
    {
        int j;
        int count;

        readerIndices[i] = i;
        count            = randomCount();
        for (j = 0 ; j < count ; ++j)
        {
            pthread_create(&readersThread[totalReaders++], NULL, reader, &readerIndices[i]);
        }
    }

    for (i = 0 ; i < writersCount ; i++)
    {
        int j;
        int count;

        writerIndices[i] = i;
        count            = randomCount();
        for (j = 0 ; j < count ; ++j)
        {
            pthread_create(&writersThread[totalWriters++], NULL, writer, &writerIndices[i]);
        }
    }

    for (i = 0 ; i < totalWriters ; i++)
    {
        pthread_join(writersThread[i], NULL);
    }

    for (i = 0 ; i < totalReaders ; i++)
    {
        pthread_join(readersThread[i], NULL);
    }

    printf("--------------\n");
    for (i = 0 ; i < readersCount ; i++)
    {
        printf("Reader %d read %d times\n", i + 1, readCount[i + 1]);
    }

    for (i = 0 ; i < writersCount ; i++)
    {
        printf("Writer %d wrote %d times\n", i + 1, readCount[i + 1]);
    }

    sem_destroy(&w);
    sem_destroy(&m);
    return 0;
}
Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97