1

I want to create a program where there are 2 threads reader and writer.
Both reader and writer runs for 10 iterations.
I need a synchronization mechanism that can enable reader and writer execute alternatively.
I tried only with mutex but it did not work. So I used conditional wait, still I could not achieve the results and the code is in infinite wait state. Below is the code that I tried.

int my_global = 0;
int write_flag = 0;
pthread_mutex_t mutex;
pthread_cond_t mycond;

void *reader() {
    for(int j=0;j<10;j++){
        pthread_mutex_lock(&mutex);
        printf("\nReader - value of global is %d & write flag is %d and j is %d",my_global,write_flag,j);
        write_flag = 1;
        pthread_mutex_unlock(&mutex);
        pthread_cond_signal(&mycond);
        // sleep(1);
    }
}

void *writer(){
    for(int k=0;k<10;k++){
        pthread_mutex_lock(&mutex);
        printf("\nstarted writer");
        while(write_flag!=1){
            pthread_cond_wait(&mycond,&mutex);
        }
        write_flag = 0;
        my_global++;
        printf("\nwriter done and writeflag is %d and global is %d and k is %d",write_flag,my_global,k);
        pthread_mutex_unlock(&mutex);
    }
}


int main()
{
    pthread_t my_reader;
    pthread_t my_writer;
    pthread_cond_init(&mycond,NULL);
    pthread_mutex_init(&mutex,NULL);
    if (pthread_create(&my_reader, NULL, &reader, NULL) != 0) {
        perror("Failed to create thread");
    }
    if (pthread_create(&my_writer, NULL, &writer, NULL) != 0) {
        perror("Failed to create thread");
    }
    if (pthread_join(my_reader, NULL) != 0) {
        perror("Failed to join thread");
    }
    if (pthread_join(my_writer, NULL) != 0) {
        perror("Failed to join thread");
    }
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&mycond);
    return 0;
}
6
  • 1
    This is what semaphores are for. Commented Sep 17, 2024 at 17:04
  • Unrelated bug: The pthread_* functions don't set errno, so your use of perror is wrong. You should do if ( errno = pthread_foo( ... ) ) { perror( ... ); exit( 1 ); } Commented Sep 18, 2024 at 11:40
  • Unrelated bug: void *reader() { } should be void *reader( void *arg ). You can use (void)arg; in the function to avoid warnings. Same for writer. Commented Sep 18, 2024 at 11:41
  • Tip: There's no reason to unlock the mutex at the end of the loop only to lock it again at the start of the loop. Lock it before the loop and unlock it after the loop instead. Commented Sep 18, 2024 at 11:42
  • Tip: You can use pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; to initialize a mutex in static storage instead of using pthread_mutex_init. Similarly, you can use pthread_cond_t cond = PTHREAD_COND_INITIALIZER; to initialize a cond var in static storage instead of using pthread_cond_init. Commented Sep 18, 2024 at 11:42

3 Answers 3

2

You forgot to wait in the writer.


Imagine if you had a queue instead of a single variable.

What do the threads need to wait for?

  • The writer must wait for space to be available in the queue.
  • The reader must wait for items to be available in the queue.

my_global can be seen as a queue of one element if it's paired with a flag indicating the number of elements in the queue i.e. if there's an element in my_global waiting to be consumed.


Rather than hardcode ten iterations in the reader, I would add the following condition:

  • The reader must wait for writer to have finished adding items.

This will require a flag indicating whether the writer is done or not.


ok, now we have all the elements we need.

Our shared variables are going to be

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  cond  = PTHREAD_COND_INITIALIZER;

// Shared variables access-controlled by `mutex`.
int done        = 0;
int buffer_full = 0;
int buffer;

Our writer's wait loop is going to be

while ( buffer_full )
   pthread_cond_wait( &cond, &mutex );

Our reader's wait loop is going to be

while ( !buffer_full && !done )
   pthread_cond_wait( &cond, &mutex );

Specifically, the following is how I'd write the reader and writer:

void *reader( void *arg ) {
   (void)arg;

   pthread_mutex_lock( &mutex );

   for ( int i=0; i<10; i++ ) {
      while ( buffer_full )
         pthread_cond_wait( &cond, &mutex );

      buffer = i;
      buffer_full = 1;
      pthread_cond_signal( &cond );
   }

   done = 1;
   pthread_cond_signal( &cond );

   pthread_mutex_unlock( &mutex );
}
void *writer( void *arg ) {
   (void)arg;

   pthread_mutex_lock( &mutex );

   while ( 1 ) {
      while ( !buffer_full && !done )
         pthread_cond_wait( &cond, &mutex );

      if ( buffer_full ) {
         printf( "%d\n", buffer );
         buffer_full = 0;
      }

      pthread_cond_signal( &cond );

      if ( done )
         break;
   }

   pthread_mutex_unlock( &mutex );
}

Here is a thread-safe queue and a demo.

Sign up to request clarification or add additional context in comments.

3 Comments

I have the full thing coded. Come back in a week and post a comment here if you want to see it.
Hi ikegami, Thank you for your reply. Please post the full code so that I could get better understanding.
@Mughunth Srinivasan, Added
1

In addition to the issues already mentioned, you have a severe bug in the form of wrong callback function format. Pthreads require a function of the form void* func (void*). You are using void* func ().

The reason why you aren't getting any compiler warning is because in older C versions, an empty parameter list function pointer is compatible with a function pointer with the same type but any parameter list. But just because the pointer types are compatible doesn't mean it will work, because the functions might end up using the wrong calling convention, which in turn can lead to mysterious run-time crashes etc.

If you compile in C23 mode, this bug will get detected.

Comments

0

I have found the solution for my problem.

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

int shared_var = 0;
int turn = 0; // 0 = writer's turn, 1 = reader's turn

pthread_mutex_t lock;
pthread_cond_t cond;

void* writer(void* arg) {
     for(int j=0;j<10;j++){
        pthread_mutex_lock(&lock);
        while (turn != 0)
            pthread_cond_wait(&cond, &lock);

        shared_var++;
        printf("Writer: updated shared_var to %d\n", shared_var);
        turn = 1; // Give turn to reader
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }

    pthread_exit(NULL);
}

void* reader(void* arg) {
    for(int k=0;k<10;k++){
        pthread_mutex_lock(&lock);
        while (turn != 1)
            pthread_cond_wait(&cond, &lock);

        printf("Reader: read shared_var = %d\n", shared_var);

        turn = 0; // Give turn to writer
        pthread_cond_signal(&cond);
        pthread_mutex_unlock(&lock);
    }

    pthread_exit(NULL);
}

int main() {
    pthread_t reader_thread, writer_thread;

    pthread_mutex_init(&lock, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&writer_thread, NULL, writer, NULL);
    pthread_create(&reader_thread, NULL, reader, NULL);

    pthread_join(writer_thread, NULL);
    pthread_join(reader_thread, NULL);

    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);

    printf("Program finished.\n");

    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.