Как использовать мьютексы в C - PullRequest
0 голосов
/ 05 сентября 2011

Я запутался в использовании нескольких мьютексов в 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;
}

- это

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

, что я хочу, это

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

Ответы [ 3 ]

5 голосов
/ 05 сентября 2011

Когда вы звоните pthread_create, вы уже заблокировали mutex1.Это означает, что каждый другой поток, который вызывает pthread_mutex_lock(&mutex1);, будет ожидать разблокировки мьютекса.Вот что происходит, когда вы создаете второй поток: mutex1 уже заблокирован, поэтому второй поток не может войти в критическую секцию, но ему нужно дождаться разблокировки мьютекса.Это происходит в конце функции main.

Вам потребуется реорганизовать код, чтобы получить желаемый вывод.

Однако, чтобы получить такой результат, вы должны проверить системы синхронизации, например семафоры или условные переменные ;они обеспечат более ясный и простой способ синхронизации потоков.
Вы также можете проверить этот учебник: Программирование потоков POSIX


Простое решение с использованием семафоров (не проверено, нодолжно работать):

#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;
}
3 голосов
/ 05 сентября 2011

Мьютексы сами по себе не подходят для выполнения требуемого тесно взаимосвязанного исполнения - их обычное использование - для защиты доступа к общей структуре данных.Это потому, что они предназначены для того, чтобы сказать «Вещь А не должна происходить одновременно с Вещью В» , но они ничего не говорят о том, произойдет ли Вещь А или Вещество B первым или вторым.

Вы можете использовать мьютексы и условные переменные, но в этом случае ваша проблема наиболее близко соответствует объекту pthreads Barrier:

#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;
}

Обратите внимание, что это , а не обычно стараются жестко заблокировать выполнение потоков таким образом - действительно, вы приложили большие усилия, чтобы гарантировать, что потоки вообще не будут выполняться параллельно, что является главной целью потоков в первую очередь.Если вы обнаружите, что делаете это в реальном проекте, это признак того, что вам следует тщательно переосмыслить свой дизайн.

2 голосов
/ 05 сентября 2011

Я думаю, вам нужно разблокировать mutex1 раньше. Вы разблокируете его после printf("Parent 2\n");, так что thread1 все еще заблокирован в ожидании pthread_mutex_lock(&mutex1);.

Когда запускается thread1, его первым шагом является блокировка, пока он ожидает взаимное исключение (ключ в названии) блокировки на mutex1. Так что это приостановлено.

Тогда вы:

    printf("Parent 1\n");

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