Мьютекс

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 11:24, 29 октября 2016.

Мью́текс (англ. mutex, от англ. mutual exclusion — «взаимное исключение») — является аналогом одноместного семафора, в программировании необходим для сопоставления синхронно выполняющихся потоков.[1]. Мью́текс представляет собой концепцию программирования, которая используется для решения вопросов многопоточности. Мьютекс отличается от семафора тем, что допускает только один поток в контролируемом участке, заставляя другие потоки, которые пытаются получить доступ к этому разделу ждать, пока первый поток не вышел из этого раздела.

Принимает два значенения:

  • открыт - поток может войти в свою критическую секцию;
  • закрыт - поток не может войти в критическую секцию.

Задача мьютекса — защита объекта от доступа к нему других потоков, отличных от того, который завладел мьютексом. В каждый конкретный момент только один поток может владеть объектом, защищённым мьютексом. Если другому потоку будет нужен доступ к переменной, защищённой мьютексом, то этот поток засыпает до тех пор, пока мьютекс не будет освобождён.

Цель использования мьютексов — защита данных от повреждения в результате асинхронных изменений (состояние гонки), однако могут порождаться другие проблемы — такие, как взаимная блокировка (клинч). Для описания мьютекса требуется всего один бит, хотя чаще используется целая переменная, у которой 0 означает не блокированное состояние, а все остальные значения соответствуют блокированному состоянию. Значение мьютекса устанавливается двумя процедурами. Если поток собирается войти в критическую область, он вызывает процедуру mutex_lock. Если мьютекс не заблокирован, запрос выполняется и вызывающий поток может попасть в критическую область[2]. Если mutex закрыт, то поток пытающийся войти в критическую секцию блокируется.

Мьютекс в операционных системах

Использование mutex в Windows

  • Создание mutex
    CreateMutex (параметры_безопасности, собственность_потока, имя)
    Поименный mutex может использоваться для синхронизации процессов
  • Открытие mutex
    OpenMutex (параметры_безопасности, собственность_потока, имя)
  • Ожидание и захват mutex
    WaitForSingleObject (дескриптор_mutex, время_ожидания)
  • Освобождение mutex
    ReleaseMutex (дескриптор_mutex)

Использование mutex в Linux

Mutex в Linux - это объект для синхронизации потоков в рамках одного процесса

  • Описание mutex
    pthread_mutex_t_mutex;
  • Инициализация mutex
    pthread_mutex_init (&mutex, NULL);
  • Перед входом в критическую секцию поток должен попытаться захватить mutex. Если это не удается, то поток блокируется до освобождения mutex
    pthread_mutex_lock (&mutex);
  • При выходе из критической секции поток должен освободить mutex
    pthread_mutex_unlock (&mutex);
Методы[3]
Имя Описание
Close() Освобождает все ресурсы, удерживаемые текущим объектом WaitHandle.(Наследуется от WaitHandle.)
CreateObjRef(Type) Создает объект, который содержит всю необходимую информацию для создания прокси-сервера, используемого для взаимодействия с удаленным объектом.(Наследуется от MarshalByRefObject.)
Dispose() Освобождает все ресурсы, используемые текущим экземпляром класса WaitHandle.(Наследуется от WaitHandle.)
Equals(Object) Определяет, равен ли заданный объект текущему объекту.(Наследуется от Object.)
GetAccessControl() Получает объект MutexSecurity, представляющий безопасность управления доступом для именованного мьютекса.
GetHashCode() Играет роль хэш-функции для определённого типа.

(Наследуется от Object.)

GetLifetimeService() Извлекает объект обслуживания во время существования, который управляет политикой времени существования данного экземпляра.(Наследуется от MarshalByRefObject.)
GetType() Возвращает объект класса Type для текущего экземпляра. (Наследуется от Object.)
InitializeLifetimeService() Возвращает объект обслуживания во время существования для управления политикой времени существования данного экземпляра.(Наследуется от MarshalByRefObject.)
OpenExisting(String) Открывает указанный именованный мьютекс, если он уже существует.
OpenExisting(String, MutexRights) Открывает указанный именованный мьютекс, если он уже существует, с требуемыми правами доступа.
ReleaseMutex() Освобождает объект Mutex один раз.
SetAccessControl(MutexSecurity) Задает безопасность управления доступом для именованного системного мьютекса.
ToString() Возвращает строковое представление текущего объекта.

(Наследуется от Object.)

TryOpenExisting(String, Mutex) Открывает указанный именованный мьютекс, если он уже существует, и возвращает значение, указывающее, успешно ли выполнена операция.
TryOpenExisting(String, MutexRights, Mutex) Открывает указанный именованный мьютекс, если он уже существует, с требуемыми правами доступа, и возвращает значение, указывающее, успешно ли выполнена операция.
WaitOne() Блокирует текущий поток до получения сигнала объектом WaitHandle.(Наследуется от WaitHandle.)
WaitOne(Int32) Блокирует текущий поток до получения текущим дескриптором WaitHandle сигнала, используя 32-разрядное целое число со знаком для указания интервала времени в миллисекундах.(Наследуется от WaitHandle.)
WaitOne(Int32, Boolean) Блокирует текущий поток до получения сигнала текущим объектом WaitHandle, используя 32-разрядное целое число со знаком для задания периода времени и указывая, следует ли выйти из домена синхронизации до начала ожидания.(Наследуется от WaitHandle.)
WaitOne(TimeSpan) Блокирует текущий поток до получения сигнала текущим экземпляром, используя значение типа TimeSpan для указания интервала времени.(Наследуется от WaitHandle.)
WaitOne(TimeSpan, Boolean) Блокирует текущий поток до получения сигнала текущим экземпляром, используя значение типа TimeSpan для задания интервала времени и указывая, следует ли выйти из домена синхронизации до начала ожидания.(Наследуется от WaitHandle.)

Свойства

  • Запоминание владельца - освободить мьютекс может только поток, который его захватил;
  • Повторяющийся характер - поток может неоднократно захватить мьютекс (вызвать aquire()); для освобождения мьютекса поток должен соответствующее число раз захватить release();
  • Наследование приоритета - поток, который захватил мьютекс, временно получает максимальный из приоритетных потоков, которые ждут освобождения этого мьютекса.

Структура управления мьютексом

Каждый мьютекс ассоциируется со структурой управления:

typedef struct _TN_MUTEX_S
{
    CDLL_QUEUE_S        wait_queue;
    CDLL_QUEUE_S        mutex_queue;
    CDLL_QUEUE_S        lock_mutex_queue;
    TN_UWORD            attr;
 
    TN_TCB_S          * holder;
    TN_UWORD            ceil_priority;
    TN_WORD             cnt;
    TN_OBJ_ID           id_mutex;
} TN_MUTEX_S;
Элементы структуры мьютекса
wait_queue Очередь задач, ожидающих освобождение мьютекса
mutex_queue Элемент списка заблокированных задачей мьютексов
ock_mutex_queue Системная очередь заблокированных мьютексов
attr Атрибут (тип обхода инверсии приоритетов) мьютекса
holder Указатель на TCB(Task Control Block) задачи, блокирующей мьютекс
ceil_priority Максимальный приоритет из задач, которые могут использовать ресурс (требуется для протокола увеличения приоритета)
cnt Зарезервировано
id_mutex Поле идентификации объекта как мьютекса

Структура мьютекса доступна только при определении TN_DEBUG. Тем не менее, прямой доступ к элементам структуры мьютекса крайне не рекомендуется, так как это является вмешательством в работу планировщика и других сервисов RTOS.[4]

Синхронизация файловых операций Mutex

Задача сводится к тому как заставить две программы работать с одним файлом, когда одна программа может писать, а вторая должна читать. Задача как избежать конфликтов при данной ситуации. Создадим два проекта, как Win32 console, один с именем WriteData, а другой с именем ReadData в каталоге TestMutex. Так будет и в прилагаемом проекте.

Код WriteFile:

// WriteData.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
#include "fstream.h"
#include "iostream.h"

void main()
{
	HANDLE hShared = CreateMutex(NULL, TRUE, "WriteData"); 	
	ofstream ofs("d:\\write.txt");
	ofs << "TestDataWrite" << endl;
	ofs.close();
	int i;
	cout << "Press Key and Enter for access to file " << endl;
	cin >> i; 
	ReleaseMutex(hShared); 
	CloseHandle(hShared);
}

Основа программы функция CreateMutex:

 
HANDLE CreateMutex(  
	LPSECURITY_ATTRIBUTES,	// атрибуты
	BOOL bInitialOwner,	// инициализация
	LPCTSTR lpName		// имя объекта
);

Теперь весь этот механизм запускается с помощью ReleaseMutex после использования указатель HANDLE нужно закрыть CloseHandle.

Код ReadData:

// ReadData.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
#include "fstream.h"
#include "iostream.h"

void main()
{
	HANDLE hShared = OpenMutex(MUTEX_ALL_ACCESS, FALSE, "WriteData"); 
	cout << "Wait !!!! Write File Data Proccess " << endl;
	WaitForSingleObject(hShared, INFINITE); 
	ifstream ifs("d:\\write.txt");
	char buffer[100];
	ifs >> buffer;
	cout << "Read File Data  -  " << buffer << endl;
	CloseHandle(hShared);	
}

Для доступа к Mutex теперь его нужно открыть с тем же именем. И поставить флаг MUTEX_ALL_ACCESS. Функция WaitForSingleObject будет стоять пока доступ не будет получен.

Запустите одновременно две программы. Программа чтения будет ждать пока программа записи не разрешит доступ. Вот примерно так как на экране.

подпись

Введите букву в программу записи и нажмите Enter, и программа чтения тут же опомнится и прочитает данные. Вот оно - понятие синхронизации. Кстати, данный механизм можно применять не только для записи/чтения файлов, но и для любой синхронизации между потоками, программами или еще чем.

Разница между мьютексом и Семафор (Операционные Системы)

Семафор работает в режиме пользователя и блокирует задачи. Мьютекс работает в режиме ядра и блокирует сам ресурс. На время выполнения мьютекса, задача, которая захватила мьютекс получает максимальный приоритет, соответственно, если задача выполняет какие-либо действия внутри семафора, то более высокоприоритетная задача может прервать его выполнение, если задача захватила мьютекс, то прекратить выполнение пока она освободит мьютекс, не может никакая другая задача.

Примечания

  1. Мьютекс [Электронный ресурс] : Материал из Википедии — свободной энциклопедии : Версия 76967658, сохранённая в 21:12 UTC 7 марта 2016 / Авторы Википедии // Википедия, свободная энциклопедия. — Электрон. дан. — Сан-Франциско: Фонд Викимедиа, 2016. — Режим доступа: http://ru.wikipedia.org/?oldid=76967658
  2. http://www.myshared.ru/slide/651615/
  3. https://msdn.microsoft.com/ru-ru/library/system.threading.mutex(v=vs.110).aspx
  4. http://wiki.pic24.ru/doku.php/tnkernel/ref/mutex/intro?do=backlink