Если сразу несколько потоков попытаются выполнить этот блок кода
Рис. 30.2.
Наконец, стоит отметить, что закрытие мьютекса является скорее рекомендацией, а не требованием. Иными словами, поток может проигнорировать использование мьютекса и просто обратиться к соответствующим разделяемым переменным. Но, чтобы задействовать эти переменные безопасным образом, все потоки должны совместно пользоваться мьютексом, соблюдая правила закрытия, которые тот навязывает.
30.1.1. Статически выделяемые мьютексы
Мьютекс должен быть выделен в виде статической переменной или создан динамически во время выполнения программы (например, в блоке памяти выделенном с помощью вызова malloc()). Динамическое создание мьютексов является несколько более сложным, поэтому мы отложим его обсуждение до подраздела 30.1.5.
Мьютекс представляет собой переменную типа pthread_mutex_t. Прежде чем вы сможете его использовать, он должен быть инициализирован. В случае со статически выделенным мьютексом это можно сделать путем присваивания ему значения PTHREAD_MUTEX_INITIALIZER, как показано ниже:
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
Согласно стандарту SUSv3 применение операций, которые будут описаны в оставшейся части этого раздела, к копии мьютекса приводит к неопределенным результатам. Эти операции всегда должны выполняться с оригиналом мьютекса, инициализированным статически, с помощью значения PTHREAD_MUTEX_INITIALIZER, или динамически, путем вызова pthread_mutex_init() (описанного в разделе 30.1.5).
30.1.2. Закрытие и открытие мьютекса
После инициализации мьютекс находится в открытом состоянии. Для его закрытия и открытия используются функции pthread_mutex_lock() и pthread_mutex_unlock().
#include
int pthread_mutex_lock(pthread_mutex_t *
int pthread_mutex_unlock(pthread_mutex_t *
Обе возвращают 0 при успешном завершении или положительное число, если случилась ошибка
Чтобы закрыть
Если вызывающий поток сам закрыл мьютекс, переданный в функцию pthread_mutex_lock(), тогда, если это стандартный тип мьютекса, тогда случится одно из двух, в зависимости от реализации: либо поток войдет в состояние взаимного блокирования, пытаясь закрыть мьютекс, которым он уже владеет, либо вызов завершится неудачей и вернет ошибку EDEADLK. В Linux по умолчанию происходит взаимное блокирование (другие возможные варианты будут описаны после рассмотрения разных типов мьютексов в подразделе 30.1.7).
Функция pthread_mutex_unlock() открывает мьютекс, закрытый ранее вызывающим потоком. Если мьютекс уже открыт или был закрыт другим потоком, данная функция возвращает ошибку.
Если мьютекс, открытый pthread_mutex_unlock(), ожидают сразу несколько потоков, получить его может любой из них.
Пример программы
Ниже представлена измененная версия листинга 30.1, которая использует мьютекс для защиты доступа к глобальной переменной glob. Запустив эту программу с тем же аргументом командной строки, что и раньше, мы увидим, что инкрементация значения glob всегда происходит предсказуемо:
$ ./thread_incr_mutex 10000000
glob = 20000000
Листинг 30.2. Использование мьютекса для защиты доступа к глобальной переменной
threads/thread_incr_mutex.c
#include
#include "tlpi_hdr.h"
static int glob = 0;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static void * /* 'arg' раз инкрементируем 'glob' */
threadFunc(void *arg)
{
int loops = *((int *) arg);
int loc, j, s;
for (j = 0; j < loops; j++) {
s = pthread_mutex_lock(&mtx);
if (s!= 0)
errExitEN(s, "pthread_mutex_lock");
loc = glob;
loc++;
glob = loc;
s = pthread_mutex_unlock(&mtx);
if (s!= 0)
errExitEN(s, "pthread_mutex_unlock");
}
return NULL;
}
int
main(int argc, char *argv[])
{
pthread_t t1, t2;
int loops, s;
loops = (argc > 1)? getInt(argv[1], GN_GT_0, "num-loops"): 10000000;
s = pthread_create(&t1, NULL, threadFunc, &loops);
if (s!= 0)
errExitEN(s, "pthread_create");
s = pthread_create(&t2, NULL, threadFunc, &loops);
if (s!= 0)
errExitEN(s, "pthread_create");
s = pthread_join(t1, NULL);
if (s!= 0)
errExitEN(s, "pthread_join");
s = pthread_join(t2, NULL);
if (s!= 0)
errExitEN(s, "pthread_join");
printf("glob = %d\n", glob);
exit(EXIT_SUCCESS);
}