Глобальная переменная не меняется в потоке? - PullRequest
0 голосов
/ 29 мая 2018

Я пытаюсь обновить глобальную переменную в основной функции, и поток сообщает мне, когда эта переменная положительна.

Код: https://pastebin.com/r4DUHaUV

Когда я запускаю его, отображается только 2, хотя 1 и 2 должны быть правильными.

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

pthread_t tid;
pthread_mutex_t mtx;
pthread_cond_t cond;
int nr=0;

void* function(void* arg)
{
        pthread_mutex_lock(&mtx);
        printf("Number in thread : %d \n",nr);
        while(nr<=0)
                pthread_cond_wait(&cond,&mtx);
        printf("Number %d is positive \n",nr);
        pthread_mutex_unlock(&mtx);

}

int main()
{
        pthread_mutex_init(&mtx,NULL);
        pthread_create(&tid,NULL,function,NULL);
        int i;
        for(i=0;i<3;i++)
            {
                int isPos=0;
                pthread_mutex_lock(&mtx);
                if(i==0)
                        nr=nr+1;
                if(i==1)
                        nr=nr-2;
                if(i==2)
                        nr=nr+3;
                if(nr>0)
                        isPos=1;
                if(isPos==1)
                        pthread_cond_signal(&cond);
                pthread_mutex_unlock(&mtx);
            }
        pthread_join(tid,NULL);
        return 0;
}

1 Ответ

0 голосов
/ 30 мая 2018

Как я уже упоминал в общем комментарии, я повторю здесь:

Нет гарантии, что основной поток не прекратит работу, заблокировав мьютекс, изменив nr, сообщив о cv (или нет, кто-то на самом деле его ждет) и разблокирует мьютекс, все до того, как дочерний поток даже заблокирует мьютекс, а тем более начинает ждать на cv.В этом случае nr может быть 1 (или 2 и т. Д.), Когда ребенок наконец получает мьютекс.Это означает, что ваш цикл while будет пропущен (nr <= 0 не соответствует действительности), и какое бы текущее значение nr ни было напечатано при выходе.Я запустил это несколько раз и получил 1x1, 1x2 и 2x2, несколько раз. </p>

Простое исправление для этого включает использование пары cv / mtx, которую вы настроили для мониторинга измененийс main для мониторинга запуска-запуска с function.Сначала код:

Код

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

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int nr = -1;

void* function(void* arg)
{
    // signal main to start up once we start waiting
    pthread_mutex_lock(&mtx);
    nr = 0;
    pthread_cond_signal(&cond);

    // now start waiting (which will unlock the mutex as well, which means
    //  the main thread will be be able to acquire it and check nr safely
    while(nr<=0)
        pthread_cond_wait(&cond,&mtx);

    printf("Number %d is positive \n",nr);
    pthread_mutex_unlock(&mtx);
    return NULL;
}

int main()
{
    pthread_t tid;

    pthread_create(&tid,NULL,function,NULL);

    // wait until child is knowingly waiting
    pthread_mutex_lock(&mtx);
    while (nr != 0)
        pthread_cond_wait(&cond, &mtx);
    pthread_mutex_unlock(&mtx);

    // now we know the child is ready to receive signals
    int i;
    for(i=0;i<3;i++)
    {
        pthread_mutex_lock(&mtx);

        if(i==0)
            nr=nr+1;
        if(i==1)
            nr=nr-2;
        if(i==2)
            nr=nr+3;

        int isPos = (nr>0);
        pthread_mutex_unlock(&mtx);

        if (isPos)
            pthread_cond_signal(&cond);
    }
    pthread_join(tid,NULL);
    return 0;
}

Как это работает

Начальное значение nrустановлен как -1.Только дочерний поток изменит это непосредственно на 0, и даже тогда только под защитой мьютекса предиката.

    // signal main to start up once we start waiting
    pthread_mutex_lock(&mtx);
    nr = 0;
    pthread_cond_signal(&cond);

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

    while(nr<=0)
        pthread_cond_wait(&cond,&mtx);

Теперь, вернувшись в main, автозагрузка создает дочерний поток, получаетмьютекс, затем контролирует, пока nr не станет равным нулю.

    pthread_create(&tid,NULL,function,NULL);

    // wait until child is knowingly waiting
    pthread_mutex_lock(&mtx);
    while (nr != 0)
        pthread_cond_wait(&cond, &mtx);
    pthread_mutex_unlock(&mtx);

Единственный способ сделать это - это когда nr == 0.Когда это происходит, дочерний элемент, должно быть, изменил его, но, что более важно, он также должен ожидать переменную условия (именно так мы получили мьютекс; помните?) С этого момента код похож.Стоит отметить, что я использую инициализаторы pthread, чтобы обеспечить правильную работу mutex и cvar.В исходном сообщении отсутствовала инициализация cvar.

Наконец, выполнение двойного задания с несколькими предикатами с одной парой cvar-mtx легко , чтобы испортить, и может быть очень трудно обнаружить крайние случаи, когда вы это сделали (напутали, то есть).Быть осторожен.Этот конкретный пример представляет собой последовательность обязанностей, а не одновременных обязанностей, что делает его довольно тривиальным, поэтому мне удобно показывать его.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...