Как я уже упоминал в общем комментарии, я повторю здесь:
Нет гарантии, что основной поток не прекратит работу, заблокировав мьютекс, изменив 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 легко , чтобы испортить, и может быть очень трудно обнаружить крайние случаи, когда вы это сделали (напутали, то есть).Быть осторожен.Этот конкретный пример представляет собой последовательность обязанностей, а не одновременных обязанностей, что делает его довольно тривиальным, поэтому мне удобно показывать его.
Надеюсь, это поможет.