Мутексы Программирование с использованием POSIX thread library
Мутексы (mutex) pthread_mutex_init(3C) pthread_mutex_lock(3C) pthread_mutex_trylock(3C) pthread_mutex_unlock(3C) pthread_mutex_destroy(3C) Сокращение от mutual exclusion (взаимное исключение) lock/unlock должна делать одна и та же нить.
pthread_mutex_init(3C) #include int pthread_mutex_init( pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); int pthread_mutex_destroy( pthread_mutex_t *mutex); pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
Pthread_mutex_lock(3C) #include int pthread_mutex_lock( pthread_mutex_t *mutex); int pthread_mutex_trylock( pthread_mutex_t *mutex); int pthread_mutex_unlock( pthread_mutex_t *mutex);
Работа с мутексами Мутекс предназначен для защиты критических секций (работы с разделяемыми данными) Мутекс похож на дорожный светофор: он сигнализирует, что вам не следует идти через дорогу, но не ставит физических препятствий для перехода Использование мутексов на совести программиста
Мертвые блокировки Мертвая блокировка в пределах одной нити Class monitor { private: pthread_mutex_t mx; Public: int method1() { lock(&mx); … unlock(&mx); } int method2() { lock(&mx); … method1(); … Unlock(&mx); } }
Мертвая блокировка в пределах одной нити Два способа pthread_attr_settype(PTHREAD_MUTEX_ RECURSIVE); pthread_attr_settype(PTHREAD_MUTEX_ ERRORCHECK);
Мертвая блокировка нескольких нитей Thread1 lock(&a); lock(&b) Thread2 lock(&b); lock(&a) PTHREAD_MUTEX_ERRORCHECK от этого не помогает
Способы борьбы с мертвой блокировкой Разрешить захват только одного мутекса Захватывать мутексы всегда в определенном порядке (запретить программы вида lock(&b); lock(&a); Атомарный захват нескольких мутексов
Что нельзя делать Нельзя использовать trylock (живая блокировка) Не особо много пользы от проверки на уровне библиотеки (опасность живой блокировки) Нельзя отбирать мутексы у других процессов (мы не знаем, в каком состоянии ресурс, защищаемый этим мутексом)
Упорядочение мутексов Нарушает инкапсуляцию (при захвате каждого мутекса мы должны знать обо всех мутексах, которые мы можем захватить, пока будем держать этот) Тем не менее, удобно при строго послойной организации приложения (layer1 зовет layer2, но никогда не наоборот) Возникают проблемы при использовании callback/hook и виртуальных методов
Атомарный захват группы мутексов В POSIX thread API штатных средств нет В других многопоточных API средства бывают –semop(2) в System V IPC –WaitForMultipleObjects в Win32 Можно реализовать самому при помощи условных переменных (рассматривается далее) Тоже нарушает инкапсуляцию. Послойная организация приложения не помогает.
pthread_mutexattr_t #include int pthread_mutexattr_init( pthread_mutexattr_t * attr); int pthread_mutexattr_destroy( pthread_mutexattr_t *attr);
Операции над pthread_mutexattr_t pthread_mutexattr_get/settype pthread_mutexattr_get/setpshared pthread_mutexattr_get/setprotocol pthread_mutexattr_get/setprioceiling pthread_mutexattr_get/setrobust_np
pthread_mutexattr_get/settype #include int pthread_mutexattr_gettype( pthread_mutexattr_t *restrict attr, int *restrict type); int pthread_mutexattr_settype( pthread_mutexattr_t *_attr, int type);
Типы PTHREAD_MUTEX_NORMAL –никаких проверок PTHREAD_MUTEX_ERRORCHECK –Проверка на unlock свободного мутекса –Проверка на unlock мутекса, занятого другой нитью –Проверка на lock мутекса, занятого вашей нитью PTHREAD_MUTEX_RECURSIVE –Допускает парные lock/unlock в одной нити PTHREAD_MUTEX_DEFAULT –Обычно PHTREAD_MUTEX_NORMAL
pthread_mutexattr_get/setpshared #include int pthread_mutexattr_getpshared( const pthread_mutexattr_t *restrict attr, int *restrict pshared); int pthread_mutexattr_setpshared( pthread_mutexattr_t * attr, int pshared); PTHREAD_PROCESS_SHARED PTHREAD_PROCESS_PRIVATE
pthread_mutexattr_get/setrobust_np #include int pthread_mutexattr_getrobust_np( const pthread_mutexattr_t *attr, int *robustness); int pthread_mutexattr_setrobust_np( pthread_mutexattr_t *attr, int robustness);
Robustness PTHREAD_MUTEX_ROBUST_NP PTHREAD_MUTEX_STALLED_NP
Что означает ROBUST Если процесс, удерживающий мутекс, умер, то ресурс, защищенный этим мутексом, остается в несогласованном состоянии Попытки его захватить возвращают EOWNERDEAD Если ресурс удалось восстановить, следует вызвать функцию pthread_mutex_consistent_np
pthread_mutex_consistent_np int pthread_mutex_consistent_np( pthread_mutex_t *mutex);
pthread_mutexattr_get/setprotocol #include int pthread_mutexattr_getprotocol( const pthread_mutexattr_t *restrict attr, int *restrict protocol); int pthread_mutexattr_setprotocol( pthread_mutexattr_t *attr, int protocol);
Протоколы PTHREAD_PRIO_NONE, PTHREAD_PRIO_INHERIT, PTHREAD_PRIO_PROTECT
Инверсия приоритета INHERIT – наследование приоритета Нить, удерживающая мутекс, исполняется с приоритетом, наивысшим среди всех нитей, ждущих этого мутекса (включая себя)
Инверсия приоритета PROTECT – потолок приоритета (priority ceiling) Нить, удерживающая мутекс, исполняется с наивысшим приоритетом из всех нитей, которые могут удерживать этот мутекс.
pthread_mutexattr_get/setprioceiling #include int pthread_mutexattr_getprioceiling( const pthread_mutexattr_t *restrict attr, int *restrict prioceiling); int pthread_mutexattr_setprioceiling( pthread_mutexattr_t *attr, int prioceiling, int *oldceiling);