Как работает этот пример?
В книге Таненбаума "Современные операционные системы" есть глава про условные переменные. В данной главе автор рассказывает о их существовании и наводит пример их использования (производитель производит в буфер данные, а потребитель извлекает данные):
#include <stdio.h>
#include <pthread.h>
#define MAX 1000000000 /* Количество производимого */
pthread_mutex_t the_mutex;
pthread_cond_t condc, condp; /* используется для сигнализации */
int buffer = 0; /* буфер, используемый между производителем и потребителем */
void *producer(void *ptr) /* производство данных */
{
int i;
for (i= 1; i <= MAX; i++) {
pthread_mutex_lock(&the mutex); /* получение исключительного
доступа к буферу */
while (buffer != 0) pthread_cond_wait(&condp, &the_mutex);
buffer = i; /* помещение записи в буфер */
pthread_cond_signal(&condc); /* активизация потребителя */
pthread_mutex_unlock(&the mutex); /* освобождение доступа к буферу */
}
pthread exit(0);
}
void *consumer(void *ptr) /* потребление данных */
{
int i;
for (i = 1; i <= MAX; i++) {
pthread_mutex_lock(&the_mutex); /* получение исключительного
доступа к буферу */
while (buffer == 0 ) pthread_cond_wait(&condc, &the_mutex);
buffer = 0; /* извлечение записи из буфера */
pthread_cond_signal(&condp); /* активизация производителя */
pthread_mutex_unlock(&the_mutex); /* освобождение доступа к буферу */
}
pthread exit(0);
}
int main(int argc, char **argv)
{
pthread_t pro, con;
pthread_mutex_init(&the mutex, 0);
pthread_cond_init(&condc, 0);
pthread_cond_init(&condp, 0);
pthread_create(&con, 0, consumer, 0);
pthread_create(&pro, 0, producer, 0);
pthread_join(pro, 0);
pthread_join(con, 0);
pthread_cond_destroy(&condc);
pthread_cond_destroy(&condp);
pthread_mutex_destroy(&the mutex);
}
МОЙ ВОПРОС:
В начале функции мы блокируем мютекс и если после этого производитель прочитает, что buffer не равен нулю, мы входим в вечное ожидание сигнала по условной переменной, со стороны потребителя. Но мы ведь УЖЕ заблокировали мютекс, что значит потребитель БУДЕТ ОЖИДАТЬ его разблокирования. Как итог, мы попадаем в вечный wait. Что я понимаю не так?
Ответы (2 шт):
Мьютекс передается по ссылке в pthread_cond_wait, где он будет разблокирован чтобы условие могло изменится, а потом заблокирован обратно при возвращении. @user7860670
Мьютекс не выключает процессор. Это просто нечто, позволяющее объединить свою проверку и своё изменение в неделимую операцию.
boolean Flag; // Как-то мы этот флаг распашарим другим программам
...
if (!Flag)
{
Flag=true;
/*Здесь делаем все операции, завершения которых должен ждать другой поток в нашем или чужом процессе*/
}
Проблема этого флага в том, что проверка флага и присваивание Flag=true; – две разные операции процессора. Наш процесс проверил флаг, другой процесс получил управление, начал что-то делать с разделяемыми данными, его прервали, теперь наш процесс выполняет Flag=true; и начинает делать что-то с теми же разделяемыми данными, часть переменных уже обработана другим процессом, имея старые значения, наш процесс завершил работу с разделяемыми переменными, вернул управление, чужой процесс его снова получил, продолжил работу с разделяемыми переменными, обработал оставшиеся переменные, имеющие новые значения, но все эти переменные имеют смысл только вместе. Мьютекс решает эту проблему. Но никакая задержка при попытке захвата мьютекса, уже захваченного другим потоком своего или другого процесса, не происходит. Надо обычным явным ифом проверить, получился ли захват мьютекса. Или в обычном явном цикле с постусловием ждать захвата мьютекса в том же потоке, где крутится цикл. Проверка результата ранее предпринятой попытки захвата мьютекса без ещё одного его захвата уже была неделима до введения мьютексов. В CreateMutex завёрнуто только
if (Мьютекс ещё никем не захвачен)
{
Записать, что мьютекс захватили именно мы
}
, а всё остальное – отдельно.
if (Мьютекс наш)
прервать нельзя и это не свойство самого мьютекса. Если мьютекс наш, то будем делать всё, что в фигурных скобках данного ифа, а другие потоки сами не будут дёргать данные, так как у них условие будет ложно, если же мьютекс не наш, то ничего такого делать не будем, а другой поток имеет право дёргать переменные именно потому, что данный поток обойдёт скобки, не выполняя. А где у Вас проверка результата попытки захвата мьютекса?