简体   繁体   中英

C Threads to print sequence of numbers : with even and odd number printing threads running parallely

I am new to multithread programming. I tried to print sequence of numbers using even and odd number printing threads, running in parallel. When executed, the code enters a deadlock. Can anyone help me to solve this.

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

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t even, odd;

void printfun1(void *pnt);
void printfun2(void *pnt);

main()
{

    pthread_t pthread1, pthread2;
    int ret1, ret2;
    char *message = "thread 1";
    char *message2 = "thread 2";

    ret1 = pthread_create(&pthread1, NULL, printfun1, (void *)message);

    if(ret1)
    {
        printf("thread creation failed");
    }
    ret2 = pthread_create(&pthread2, NULL, printfun2,(void*) message2);
    if(ret2)
    {
        printf("thread creation failed");
    }

    pthread_join(pthread1, NULL);
    pthread_join(pthread2, NULL);

    exit(0);
}
void printfun1(void *ptr)
{

    char* message = ptr;
    int counter = -1;

    while(counter < 50)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&even, &mutex);
        counter += 2;

        printf("%d \n", counter);
        pthread_cond_signal(&odd);
        pthread_mutex_unlock(&mutex);

        usleep( 1000000);
    }
}

void printfun2(void *ptr)
{

    char* message = ptr;
    int counter2 = 0;
    pthread_cond_signal(&even);
    while(counter2 < 50)
    {
        pthread_mutex_lock(&mutex);
        pthread_cond_wait(&odd, &mutex);
        counter2 += 2;

        printf("%d \n", counter2);
        pthread_cond_signal(&even);
        pthread_mutex_unlock(&mutex);
        usleep( 1000000);
    }
}

There are at least a couple things wrong with the program:

  1. You never initialize the condition variables:

     pthread_cond_init(&even, NULL); pthread_cond_init(&odd, NULL); 
  2. You can reach a deadlock if you signal a condition when the other thread isn't waiting on that condition. Normally, when you use pthread_cond_wait() , you also are checking some other shared variable in a while loop. I rewrote your program to demonstrate this:

     #include <stdio.h> #include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t even = PTHREAD_COND_INITIALIZER; pthread_cond_t odd = PTHREAD_COND_INITIALIZER; void *printfun1(void *pnt); void *printfun2(void *pnt); int main(void) { pthread_t pthread1, pthread2; int ret1, ret2; ret1 = pthread_create(&pthread1, NULL, printfun1, NULL); if(ret1) { printf("thread creation failed"); } ret2 = pthread_create(&pthread2, NULL, printfun2, NULL); if(ret2) { printf("thread creation failed"); } pthread_join(pthread1, NULL); pthread_join(pthread2, NULL); } int counter = 0; void *printfun1(void *ptr) { while(counter < 50) { pthread_mutex_lock(&mutex); while ((counter & 1) == 1) pthread_cond_wait(&even, &mutex); printf("%d \\n", counter); counter++; pthread_cond_signal(&odd); pthread_mutex_unlock(&mutex); usleep( 1000000); } return NULL; } void *printfun2(void *ptr) { while(counter < 50) { pthread_mutex_lock(&mutex); while ((counter & 1) == 0) pthread_cond_wait(&odd, &mutex); printf("%d \\n", counter); counter++; pthread_cond_signal(&even); pthread_mutex_unlock(&mutex); usleep( 1000000); } return NULL; } 

    Now you can see how deadlock is avoided. A thread only starts waiting on a condition when it knows that it needs to wait on the condition. So even if the second thread signalled the condition when the first thread wasn't waiting on it, it doesn't matter.

I think that to do the job right, you need three mutexes and three condition variables. The odd thread must keep the odd mutex locked for the entire duration of the program. The only time the odd mutex is unlocked is when the odd thread is waiting on its condition. Likewise, the even thread needs to keep the even mutex locked for the duration.

And you need a main mutex and a main condition variable, so that the odd and even threads can signal main after locking their respective mutexes.

After the odd and even threads
- are up and running
- have locked their mutexes
- and are waiting on their condition variables (which unlocks the mutex)
then main can signal the odd thread one time to get things started.

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

pthread_mutex_t mainMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t oddMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t evenMutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t mainCond = PTHREAD_COND_INITIALIZER;
pthread_cond_t oddCond = PTHREAD_COND_INITIALIZER;
pthread_cond_t evenCond = PTHREAD_COND_INITIALIZER;

void *printOdd( void *arg )
{
    pthread_mutex_lock( &oddMutex );        // grab the odd mutex

    pthread_mutex_lock( &mainMutex );       // signal main that the odd thread
    pthread_cond_signal( &mainCond );       // is locked and ready for action
    pthread_mutex_unlock( &mainMutex );

    for ( int counter = 1; counter < 50; counter += 2 )
    {
        pthread_cond_wait( &oddCond, &oddMutex );   // wait for the odd signal
        printf( "%d\n", counter );

        pthread_mutex_lock( &evenMutex );   // signal the even thread
        pthread_cond_signal( &evenCond );
        pthread_mutex_unlock( &evenMutex );

        usleep( 100000 );
    }

    pthread_mutex_unlock( &oddMutex );
    return NULL;
}

void *printEven( void *arg )
{
    pthread_mutex_lock( &evenMutex );       // grab the even mutex

    pthread_mutex_lock( &mainMutex );       // signal main that the even thread
    pthread_cond_signal( &mainCond );       // is locked and ready for action
    pthread_mutex_unlock( &mainMutex );

    for ( int counter = 2; counter < 50; counter += 2 )
    {
        pthread_cond_wait( &evenCond, &evenMutex ); // wait for the even signal
        printf( "%d\n", counter );

        pthread_mutex_lock( &oddMutex );    // signal the odd thread
        pthread_cond_signal( &oddCond );
        pthread_mutex_unlock( &oddMutex );

        usleep( 100000 );
    }

    pthread_mutex_unlock( &evenMutex );
    return NULL;
}

int main( void )
{
    pthread_t id1, id2;

    pthread_mutex_lock( &mainMutex );                           // grab the main mutex

    if ( pthread_create( &id1, NULL, printOdd, NULL ) != 0 )    // create the odd thread
        exit( 1 );
    pthread_cond_wait( &mainCond, &mainMutex ); // wait for the signal from the odd thread

    if ( pthread_create( &id2, NULL, printEven, NULL ) != 0 )   // create the even thread
        exit( 1 );
    pthread_cond_wait( &mainCond, &mainMutex ); // wait for the signal from the even thread

    pthread_mutex_unlock( &mainMutex );     // startup has completed, release the main mutex

    pthread_mutex_lock( &oddMutex );        // signal the odd thread to get things rolling
    pthread_cond_signal( &oddCond );
    pthread_mutex_unlock( &oddMutex );

    pthread_join( id1, NULL );              // wait for the threads to finish
    pthread_join( id2, NULL );

    exit( 0 );
}

First thing is condition variable is not initialized to "PTHREAD_COND_INTIALIAZER". Coming to the program, in the first thread, i think pthread_mutex_unlock should come before pthread_cond_signal

the following code --removes the unneeded system function calls --properly handles the mutex creation/locking/unlocking/destruction --prints even/odd values from 0 through 49 --properly handles logging of errors --corrects the compiler warning about undefined function exit() --stops threads getting locked in the inner while loop --properly defines the top thread functions as 'void*' rather than 'void' --properly sets parameters to pthread_create() --properly exits the threads via pthread_exit() --and several other minor fixes

#include <stdio.h>
#include <stdlib.h>  // exit(), EXIT_FAILURE
#include <pthread.h>

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;


void *printfun1(void *);
void *printfun2(void *);

main()
{

    pthread_t pthread1, pthread2;
    int ret1, ret2;

    ret1 = pthread_create(&pthread1, NULL, &printfun1, (void *)message);

    if(ret1)
    {
        perror("thread 1 creation failed");
        exit( EXIT_FAILURE );
    }

    ret2 = pthread_create(&pthread2, NULL, &printfun2,(void*) message2);

    if(ret2)
    {
        perror("thread 2 creation failed");
        exit( EXIT_FAILURE );
    }

    pthread_join(pthread1, NULL);
    pthread_join(pthread2, NULL);
    pthread_mutex_destroy(&mutex);

    return 0;
} // end function: main



int counter = 0;

// Note:
//     1) 0 is even so should be printed
//     2) 50 is beyond the range, so should not be printed
//     3) use do{...}while so program will exit when done, 
//        rather than getting locked in wait loop
void *printfun1(void *ptr)
{
    do
    {
        while( (counter & 1) == 0 )
        {   
            usleep(100);
        }

        pthread_mutex_lock(&mutex);
        printf("%d \n", counter);
        counter++;
        pthread_mutex_unlock(&mutex);

    } while( counter < 50 );
    pthread_exit( 0 );
} // end function: printfun1



void *printfun2(void *ptr)
{
    do
    {
        while( (counter & 1) == 1 )
        { 
            usleep(100);
        }  

        pthread_mutex_lock(&mutex);
        printf("%d \n", counter);
        counter++;
        pthread_mutex_unlock(&mutex);

    } while( counter < 50 );
    pthread_exit( 0 );
} // end function: printfun2

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM