} else {
fgets(work_area, WORK_SIZE, stdin);
}
sem_post(&bin_sem);
}
Теперь, если вы введете FAST
sem_post, чтобы запустить счетчик символов, но немедленно обновит work_area чем-то другим.$ cc -D_REENTRANT thread3a.с -о thread3a -lpthread
$ ./thread3a
Input some text. Enter 'end' to finish
Excession
You input 9 characters
FAST
You input 7 characters
You input 7 characters
You input 7 characters
end
Waiting for thread to finish...
Thread joined
Проблема этой программы заключается в том, что она рассчитывала на то, что ввод текста из программы продлится так долго, что у другого потока хватит времени для подсчета символов до того, как поток main
FAST с клавиатуры и затем Wheeee..., формируемое автоматически), у второго потока не было времени для выполнения. Но семафор наращивался несколько раз, поэтому считающий поток продолжал считать слова и уменьшал значение семафора до тех пор, пока оно снова не стало нулевым.Этот пример показывает, как аккуратны вы должны быть с временны́ми условиями в многопоточных программах. Исправить программу можно, применяя дополнительный семафор для того, чтобы заставить поток main
Синхронизация с помощью мьютексов
Другой способ синхронизации доступа в многопоточных программах — применение
Базовые функции, необходимые для использования мьютексов, очень похожи на функции семафоров. Они объявляются следующим образом:
#include <рthread.h>
int pthread_mutex_init(pthread_mutex_t* mutex,
const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t* mutex);
int pthread_mutex_unlock(pthread mutex_t* mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
Как обычно, в случае успешного завершения возвращается 0 и код ошибки в случае аварийного завершения, но переменная errno
Как и функции семафоров, функции мьютексов принимают указатель на предварительно объявленный объект, в данном случае типа pthread_mutex_t
pthread_mutex_init позволяет задать атрибуты мьютекса, управляющие его поведением. По умолчанию тип атрибута — "fast". У него есть небольшой недостаток: если ваша программа попытается вызвать функцию pthread_mutex_lock для мьютекса, который уже заблокирован, программа блокируется. Поскольку поток, удерживающий блокировку, в данный момент заблокирован, мьютекс никогда не будет открыт, и программа попадает в тупиковую ситуацию. Есть возможность изменить атрибуты мьютекса так, чтобы он либо проверял наличие такой ситуации и возвращал ошибку, либо действовал рекурсивно и разрешал множественные блокировки тем же самым потоком, если будет такое же количество разблокировок в дальнейшем.Установка атрибутов мьютекса в этой книге не рассматривается, поэтому мы будем передавать NULL
pthread_mutex_init.Выполните упражнение 12.4.
Далее приводится еще одна модификация исходной программы thread1.с, но значительно измененная. На этот раз вы уделите особое внимание доступу к вашим важным переменным и примените мьютекс для того, чтобы быть уверенными в том, что они доступны в любой момент времени только одному потоку. Для легкости чтения текста примера мы пропустили некоторые проверки ошибок при возвратах из мьютекса, заблокированного и открытого. В рабочем программном коде вы обязательно должны проверять эти возвращаемые значения. Далее приведен текст новой программы thread4.c.
#include
#include
#include
#include
#include
#include
void *thread_function(void *arg);