Для досрочного завершения потока можно воспользоваться функцией pthread_cancel(3). Единственным аргументом этой функции является идентификатор потока.
16. Взаимоисключения – программный подход и аппаратная поддержка.
Одной из причин зависимости результатов выполнения программ от порядка чередования команд может быть разделение одних и тех же данных между одновременно исполняемыми процессами. Данная ситуация может рассматриваться как проявление общей проблемы использования разделяемых ресурсов (общих данных, файлов, устройств и т.п.). Для организации разделения ресурсов между несколькими процессами необходимо иметь возможность:
- определения доступности запрашиваемых ресурсов (ресурс свободен и может быть выделен для использования, ресурс уже занят одним из процессов программы и не может использоваться дополнительно каким-либо другим процессом);
- выделения свободного ресурса одному из процессов, запросивших ресурс для использования;
- приостановки (блокировки) процессов, выдавших запросы на ресурсы, занятые другими процессами.
При разработке алгоритмов взаимоисключения должны соблюдаться следующие требования: Взаимоисключения должны осуществляться в принудительном порядке Процесс, прервавший или завершающий работу вне критического раздела, не должен влиять на другие процессы Не должно возникать ситуации бесконечного ожидания входа в критическую секцию Когда в критическом разделе нет ни одного процесса, любой процесс, запросивший возможность входа в к/р должен его немедленно получить. Не должно делаться никаких предположений ни о количестве процессов, ни об их относительной скорости выполнения/Любой процесс должен находиться в к/р ограниченное время
Алгоритм Петерсона - программный алгоритм взаимного исключения потоков исполнения кода, который изначально был сформулирован для 2-х поточного случая, но может быть обобщён для произвольного количества потоков. Алгоритм условно называется программным, т.к. не основан на использовании специальных команд процессора для запрета прерываний, блокировки шины памяти и т.д., используются только общие переменные памяти и цикл для ожидания входа в критическую секцию исполняемого кода. Глобальная переменная flag указывает положение каждого процесса по отношению к взаимоисключению, а глобальная переменная turn разрешает конфликты одновременности. Алгоритм представлен в листинге:
boolean flag [2]; int turn; void P0(){ while(true){ flag[0] - true; turn = 1; while(flag[l] && turn == 1) /* Ничего не делать */; /* Критический раздел */; flagfO] = false; /* Остальной код */; } } void P1(){ while(true){ flag[l] = true; turn = 0; while(flag[0] && turn == 0} /* Ничего не делать */; /* Критический раздел */; flag[l] = false; /* Остальной код */; } } void main(){ flag[0] = false; flag[l] = false; parbegin(P0,P1); }
Выполнение условий взаимоисключения легко показать. Рассмотрим процесс РО. После того как flag[0] установлен им равным true, PI войти в критический раздел не может. Если же Р1 уже находится в критическом разделе, то flag[l] = true и для РО вход в критический раздел заблокирован. Однако взаимная блокировка в данном алгоритме предотвращена. Предположим, что РО заблокирован в своем цикле while. Это означает, что flag[l] равен true, a turn = 1. РО может войти в критический раздел, когда либо flag[l] становится равным false, либо turn становится равным О. Рассмотрим три исчерпывающих случая.
Р1 не намерен входить в критический раздел. Такой случай невозможен, поскольку при этом выполнялось бы условие flag[l] = false.
P1 ожидает вход в критический раздел. Такой случай также невозможен, поскольку если turn = 1, то Р1 способен войти в критический раздел.
Р1 циклически использует критический раздел, монополизировав доступ к нему. Этого не может произойти, поскольку Р1 вынужден перед каждой попыткой входа в критический раздел дать такую возможность процессу РО, устанавливая значение turn равным 0.
Следовательно, у нас имеется простое решение проблемы взаимных исключений для двух процессов. Впрочем, алгоритм Петерсона легко обобщается на случай n процессов.
Алгоритм Деккера
int ProcessNum=1; // номер процесса для доступа
к ресурсу
int ResourceProc1 = 0; // = 1 – ресурс занят
процессом 1
int ResourceProc2 = 0; // = 1 – ресурс занят
процессом 2
Process_1() {
while (1) {
ResourceProc1 = 1; // процесс 1 пытается занять ресурс
/* цикл ожидания доступа к ресурсу */
while ( ResourceProc2 == 1 ) {
if ( ProcessNum == 2 ) {
ResourceProc1 = 0;
// повторять, пока ресурс занят процессом 2
while ( ProcessNum == 2 );
ResourceProc1 = 1;
}
}
< Использование общего ресурса >
ProcessNum = 2;
ResourceProc1 = 0;
}
}
Process_2() {
while (1){
ResourceProc2 = 1; // процесс 2 пытается занять ресурс