Один поток может читать count
только с удержанным condition_mutex
(где count
проверяется против COUNT_HALT1
и COUNT_HALT
) или вообще без удержания мьютекса (где count
проверяется с COUNT_DONE
) в то время как другой поток может изменить count
, удерживая только count_mutex
.Этот несинхронизированный доступ приводит к неопределенному поведению, как отметил @EOF в комментариях к вопросу, поэтому код просто неверен.
При этом, даже если мы выполняем только functionCount1()
без другого потока (так чтонесинхронизированный доступ не происходит), мы все равно ожидаем увидеть этот вывод:
Counter value functionCount1: 1
Counter value functionCount1: 2
Counter value functionCount1: 3
Это потому, что значение счетчика печатается после приращения, поэтому в последней итерации он видитначальное значение счетчика 2, не ждет, увеличивает счетчик, а затем печатает новое значение счетчика 3.
Обратите внимание, что в вашем исходном коде, даже игнорируя несинхронизированный доступ, равно все еще есть возможность для functionCount1
выполнить приращение от 3 до 4. Это связано с тем, что в промежутке между functionCount1
просмотром значения count
2 и принятием решения не ждать и фактически блокированием count_mutex
значение может бытьувеличивается другим потоком.
Для удаления несинхронизированного доступа к count
и исправления гонки, указанной в предыдущем пaragraph, вы просто полностью удаляете condition_mutex
и вместо этого используете count_mutex
, сохраняя его заблокированным между возвратом pthread_cond_wait()
и фактическим приращением count
.Это общий шаблон: мьютекс, который вы заблокировали при вызове pthread_cond_wait()
, должен быть мьютексом, защищающим общее состояние, которое вы ожидаете, с помощью вашей переменной условия (здесь это общее состояние является просто переменной count
):
void *functionCount1()
{
for(;;)
{
pthread_mutex_lock( &count_mutex );
while( count >= COUNT_HALT1 && count <= COUNT_HALT2 )
{
pthread_cond_wait( &condition_cond, &count_mutex );
}
count++;
printf("Counter value functionCount1: %d\n",count);
if (count >= COUNT_DONE)
{
pthread_mutex_unlock( &count_mutex );
return(NULL);
}
pthread_mutex_unlock( &count_mutex );
}
}
void *functionCount2()
{
for(;;)
{
pthread_mutex_lock( &count_mutex );
if( count < COUNT_HALT1 || count > COUNT_HALT2 )
{
pthread_cond_signal( &condition_cond );
}
count++;
printf("Counter value functionCount2: %d\n",count);
if (count >= COUNT_DONE)
{
pthread_mutex_unlock( &count_mutex );
return(NULL);
}
pthread_mutex_unlock( &count_mutex );
}
}
Однако это не оптимально: мьютекс удерживается заблокированным дольше, чем необходимо.Если вас не волнует произвольное чередование выходных данных между потоками, тогда вам не нужно блокировать count_mutex
при вызове printf()
, если вы берете локальную копию нового значения count
для передачидо printf()
.Вы также можете использовать эту локальную копию в тесте выхода.
Кроме того, условие сигнализации необходимо проверять только после того, как functionCount2()
изменилось count
.pthread_mutex_signal()
не нужно вызывать с удержанным мьютексом, поэтому мы можем поставить его и после printf()
, используя локальную копию счета:
void *functionCount1()
{
for(;;)
{
int my_count;
pthread_mutex_lock( &count_mutex );
while( count >= COUNT_HALT1 && count <= COUNT_HALT2 )
{
pthread_cond_wait( &condition_cond, &count_mutex );
}
count++;
my_count = count;
pthread_mutex_unlock( &count_mutex );
printf("Counter value functionCount1: %d\n", my_count);
if (my_count >= COUNT_DONE)
return(NULL);
}
}
void *functionCount2()
{
for(;;)
{
int my_count;
pthread_mutex_lock( &count_mutex );
count++;
my_count = count;
pthread_mutex_unlock( &count_mutex );
printf("Counter value functionCount2: %d\n", my_count);
if ( my_count < COUNT_HALT1 || my_count > COUNT_HALT2 )
{
pthread_cond_signal( &condition_cond );
}
if (my_count >= COUNT_DONE)
return(NULL);
}
}
Как отмечалось выше, вывероятно, больше не будет видеть вывод printf()
, упорядоченный в строгом порядке подсчета, потому что мы больше не заставляем printf () выполняться атомарно с приращением.