Меняется значение элемента массива на число, которое в него не записывали

Есть два потока, один записывает в циклический буффер, другой считывает. Синхронизация делается с использование мьютексов. Вроде работает всё правильно, НО почему то в ячейку записывается одно число, а выводится другое (см. изображение). Не понимаю почему это происходит, т. к. нигде после записи данные в массиве не изменяются. При этом до вывода значения ячейки, в неё записывается 0, но выводится 3995. Помогите, пожалуйста понять, почему это происходит.

#include <pthread.h>
#include <stdio.h> // printf, scanf
#include <unistd.h>
#include <stdlib.h>

#define BUFFER_SIZE 15

pthread_mutex_t my_sync;
pthread_cond_t cond_write;
pthread_cond_t cond_read;

pthread_t thread_write;
pthread_t thread_read;

int buffer[BUFFER_SIZE];

int *ptr_read = (int *)buffer;
int *ptr_write = (int *)buffer;

int *ptr_end = (int *)buffer + BUFFER_SIZE - 1;
int *ptr_start = (int *)buffer;

int number_write = 0;

int is_empty = 1;
int is_full = 0;

int my_sleep(int t)
{
return usleep(t * 1000000);
}

double t_rand(double min, double max)
{
double range = (max - min);
double div = RAND_MAX / range;
return min + (rand() / div);
}

void *func_write()
{
while(1)
{
while (is_full) pthread_cond_wait(&cond_write, &thread_write);

pthread_mutex_lock(&my_sync);

*ptr_write = number_write;

printf("WRITE %d in %p. is_full=%d, is_empty=%d\n", *ptr_write, ptr_write, is_full, is_empty);

number_write++;
if (number_write == 10) number_write = 0;

if (ptr_write == ptr_end) ptr_write = ptr_start;
else ptr_write++;

is_full = ptr_write + 1 == ptr_read? 1: 0;
is_empty = ptr_read + 1 == ptr_write? 1: 0;
if (!is_empty) pthread_cond_signal(&cond_read);

pthread_mutex_unlock(&my_sync);

double t_sleep = t_rand(0.5, 2.0);
my_sleep(t_sleep);
}
}

void *func_read()
{
while(1)
{
while (is_empty) pthread_cond_wait(&cond_read, &thread_read);

pthread_mutex_lock(&my_sync);

printf("READ %d from %p. is_full=%d, is_empty=%d\n", *ptr_read, ptr_read, is_full, is_empty);

if (ptr_read == ptr_end) ptr_read = ptr_start;
else ptr_read++;

is_full = ptr_write + 1 == ptr_read? 1: 0;
is_empty = ptr_read + 1 == ptr_write? 1: 0;
if (!is_full) pthread_cond_signal(&cond_write);

pthread_mutex_unlock(&my_sync);

double t_sleep = t_rand(0.5, 2.0);
my_sleep(t_sleep);
}
}

int main()
{
srand(time(NULL));

pthread_mutex_init(&my_sync, NULL);

pthread_cond_init(&cond_write, NULL);
pthread_cond_init(&cond_read, NULL);

pthread_create(&thread_write, NULL, func_write, NULL);
pthread_create(&thread_read, NULL, func_read, NULL);

pthread_join(thread_write, NULL);
pthread_join(thread_read, NULL);
}

Пример работы программы


Ответы (1 шт):

Автор решения: CrazyElf

Попробуем запустить ваш код в onlinegdb:

main.c: In function ‘func_write’:
main.c:52:48: warning: passing argument 2 of ‘pthread_cond_wait’ from incompatible pointer type [-Wincompatible-pointer-types]
   52 | while (is_full) pthread_cond_wait(&cond_write, &thread_write);
      |                                                ^~~~~~~~~~~~~
      |                                                |
      |                                                pthread_t * {aka long unsigned int *}
In file included from main.c:9:
/usr/include/pthread.h:1134:59: note: expected ‘pthread_mutex_t * restrict’ but argument is of type ‘pthread_t *’ {aka ‘long unsigned int *’}
 1134 |                               pthread_mutex_t *__restrict __mutex)
      |                               ~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~

Тут явно написано, что вы передаёте аргумент типа pthread_t там, где ожидается аргумент типа pthread_mutex_t. Поэтому у вас никакой синхронизации и не происходит. Заведём собственно мьютексы и передадим их:

pthread_mutex_t thread_mutex_write;
pthread_mutex_t thread_mutex_read;
...
while (is_full) pthread_cond_wait(&cond_write, &thread_mutex_write);
                                               ^^^^^^^^^^^^^^^^^^^
...
while (is_empty) pthread_cond_wait(&cond_read, &thread_mutex_read);
                                               ^^^^^^^^^^^^^^^^^^

Теперь всё работает как положено:

WRITE 0 in 0x563b7734c1a0. is_full=0, is_empty=1
WRITE 1 in 0x563b7734c1a4. is_full=0, is_empty=1
READ 0 from 0x563b7734c1a0. is_full=0, is_empty=0
WRITE 2 in 0x563b7734c1a8. is_full=0, is_empty=1
READ 1 from 0x563b7734c1a4. is_full=0, is_empty=0
WRITE 3 in 0x563b7734c1ac. is_full=0, is_empty=1
WRITE 4 in 0x563b7734c1b0. is_full=0, is_empty=0
READ 2 from 0x563b7734c1a8. is_full=0, is_empty=0
WRITE 5 in 0x563b7734c1b4. is_full=0, is_empty=0
READ 3 from 0x563b7734c1ac. is_full=0, is_empty=0
READ 4 from 0x563b7734c1b0. is_full=0, is_empty=0
...

Я C++ не очень хорошо помню, возможно там ещё где-то ошибки есть, но по крайней мере теперь читает корректно.

→ Ссылка