mattS
mattS

Reputation: 1

How to use mutexes in C

I am confused about use of multiple mutexes in C.

int main() {

        pthread_t thread1;
        char *message1 = "Thread 1";
        int  r;
        pthread_mutex_init(&mutex1, NULL);
        pthread_mutex_init(&mutex2, NULL);

        pthread_mutex_lock(&mutex1);

        r = pthread_create( &thread1, NULL, print_message_function, (void*) message1);

        printf("Parent 1\n");


        pthread_mutex_lock(&mutex2);
        printf("Parent 2\n");
        pthread_mutex_unlock(&mutex2);


        pthread_mutex_unlock(&mutex1);


        pthread_join( thread1, NULL);

        printf("Thread 1 returns: %d\n",r);
        return 0;
}

void *print_message_function( void *str ) {

        pthread_mutex_lock(&mutex1);
        char *message;
        message = (char *) str;
        printf("Child 1 received message: %s \n", message);


        pthread_mutex_lock(&mutex2);
        printf("child 2\n");
        pthread_mutex_unlock(&mutex2);




        pthread_mutex_unlock(&mutex1);

        return NULL;
}

output is

Parent 1
Parent 2
Child 1 received message: Thread 1 
child 2
Thread 1 returns: 0

what i want is

Parent 1
Child 1 received message: Thread 1 
Parent 2
child 2
Thread 1 returns: 0

Upvotes: 0

Views: 4090

Answers (3)

Manlio
Manlio

Reputation: 10864

When you call pthread_create you have already locked mutex1. That means that every other thread that calls pthread_mutex_lock(&mutex1); will wait for the mutex to be unlocked. That is what happen when you create a second thread: mutex1 is already locked, so the second thread cannot enter the critical section but need to wait for the mutex to be unlocked. That happens at the end of the main function.

You'll need to reorganize your code to get the output you desire.

However, to obtain such a result you should check synchronization systems, such semaphores or condition variables; they will provide a clearer and easier way to synchronize threads.
You may also check this tutorial: POSIX Threads Programming


A simple solution using semaphores (not tested, but it should work):

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

sem_t sem1, sem2;

void* f(void* str) {

    sem_wait(&sem1);
    printf("Child 1 received message: %s \n",(char*)str);

    sem_post(&sem2);
    sem_wait(&sem1);

    printf("Child 2\n");

    return NULL;
}



int main (int argc, const char * argv[]) {

    pthread_t thread;
    char* message = "Thread 1";

    int r;

    sem_init(&sem1,0,0);
    sem_init(&sem2,0,0);

    r = pthread_create(&thread, NULL, f, (void*)message);
    sem_post(&sem1);
    sem_wait(&sem2);

    printf("Parent 2\n");

    sem_post(&sem1); 

    pthread_join(thread1, NULL);
    printf("Thread 1 returns: %d\n",r);

    return 0;
}

Upvotes: 5

caf
caf

Reputation: 239341

Mutexes alone aren't suitable for performing the kind of closely-interlocked execution that you want - their normal use is for protecting access to a shared data structure. This is because they're designed for saying "Thing A shouldn't happen at the same time as Thing B", but they don't say anything about whether Thing A or Thing B happens first or second.

You could use mutexes and condition variables, but in this case your problem is most closely matched by a pthreads Barrier object:

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

pthread_barrier_t barrier;

void *print_message_function( void *str )
{
        char *message;
        message = (char *) str;

        pthread_barrier_wait(&barrier); /* Barrier point 1 */
        /* (wait until parent prints first message) */

        printf("Child 1 received message: %s \n", message);

        pthread_barrier_wait(&barrier); /* Barrier point 2 */
        /* (allow parent to proceed and print second message) */

        pthread_barrier_wait(&barrier); /* Barrier point 3 */
        /* (wait for parent to print second message) */

        printf("child 2\n");

        return NULL;
}

int main()
{

        pthread_t thread1;
        char *message1 = "Thread 1";
        int  r;

        pthread_barrier_init(&barrier, NULL, 2);

        r = pthread_create( &thread1, NULL, print_message_function, (void*) message1);

        printf("Parent 1\n");

        pthread_barrier_wait(&barrier); /* Barrier point 1 */
        /* (allow child to proceed and print first message) */

        pthread_barrier_wait(&barrier); /* Barrier point 2 */
        /* (wait for child to print first message) */

        printf("Parent 2\n");

        pthread_barrier_wait(&barrier); /* Barrier point 3 */
        /* (allow child to proceed and print second message) */

        pthread_join( thread1, NULL);
        /* (wait for child to exit) */

        printf("Thread 1 returns: %d\n",r);
        return 0;
}

Note that it is not usual to try to tightly interlock the execution of threads in this way - really, you've gone to great pains to ensure that the threads don't execute in parallel at all, which is the whole point of threads in the first place. If you find yourself doing this in a real project, it's a sign that you ought to carefully re-think your design.

Upvotes: 3

Preet Sangha
Preet Sangha

Reputation: 65555

I think you need to unlock mutex1 sooner. You unlock it after the printf("Parent 2\n"); so thread1 is still locked waiting for pthread_mutex_lock(&mutex1);.

When thread1 starts it's first step is to lock while it waits for mutual exclusion (clue's in the name) lock on mutex1. So it's paused.

Then you :

    printf("Parent 1\n");

    pthread_mutex_lock(&mutex2); <-- lock 2 is unleased but thread one is waiting on mutex1
    printf("Parent 2\n");

Upvotes: 2

Related Questions