Similar presentations:
Основы взаимодействия нитей в ОС Unix
1. Основы взаимодействия нитей в ОС Unix
2.
Современные библиотеки управления нитями предусматриваютпредоставление широкого спектра средств, обеспечивающих
взаимодействия нитей:
• мьютексы
• циклические блокировки
• барьеры
• условные переменные
мьютекс – инструмент, предназначенный для
решения задач синхронизации доступа к
разделяемым ресурсам на основе организации
взаимного исключения.
3. Создание/инициализация мьютекса (1)
Мьютекс – переменная специального типа pthread_mutex_t,который определяется в файле pthread.h. Мьютекс может быть
определен, как статическая, автоматическая или динамическая
переменная.
int pthread_mutex_destroy(pthread_mutex_t *mutex)
- функция удаления мьютекса для случая его размещения в
динамической памяти(завершение работы с мьютексом, до
освобождения динамической памяти)
определенные как в статической или автоматической памяти, так и
в динамической памяти.
4. Создание/инициализация мьютекса (2)
Необходимость инициализации мьютексаint pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr)
mutex
- указатель на переменную типа мьютекс.
attr
- атрибуты мьютекса (не рассматриваем, используем NULL атрибуты по умолчанию).
Функция может инициализировать, как мьютексы определенные как в
статической или автоматической памяти, так и в динамической памяти.
Явная инициализация мьютекса (для случая, если мьютекс определен в
статической или автоматической памяти):
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER
PTHREAD_MUTEX_INITIALIZER макрос, определенный в файле pthread.h
5. Операции над мьютексами (1)
Мьютекс может находиться в одном из состояний – открыт / закрытПосле инициализации мьютекс находится в состоянии открыт
«Захват» мьютекса – перевод в состояние закрыт
int pthread_mutex_lock(pthread_mutex_t *mutex)
- происходит попытка захватить мьютекс. В случае если при обращении к
функции мьютекс был закрыт другой нитью (уже находится в состоянии
закрыт), нить обратившаяся к функции pthread_mutex_lock будет заблокирована
до те пор, пока мьютекс не будет разблокирован. Функция pthread_mutex_lock
возвращает 0 в случае успеха код ошибки #0 в случае неуспешного обращения.
int pthread_mutex_trylock(pthread_mutex_t *mutex)
- при обращении нити к данной фикции, если мьютекс был в состоянии
открыт, то нить «захватывает» данный мьютекс (он переходит в состояние
закрыт и функция возвращает 0). Если при обращении к функции мьютекс был в
состоянии закрыт (другой нитью), то блокирования нити не произойдет, а она
получит код ответа EBUSY. Обращение к функции в случае, если мьютекс уже
закрыт данной нитью, вызовет ошибку (возможно зависание)
6. Операции над мьютексами (2)
«Освобождение» мьютекса – перевод в состояние открытОсвободить мьютекс может только та нить, которая его закрыла!
int pthread_mutex_unlock(pthread_mutex_t *mutex)
- функция корректно прорабатывает, если к ней обращается нить, которая
перевела данный мьютекс в состояние закрыт в этом случае код ответа 0. В
противном случае (обращение к открытому мьютексу или закрытому другим
процессом) обращение считается ошибочным – код ответа #0.
7. Простой пример синхронизации нитей (1)
В программе порождается 10 одновременно работающих нитей.Нити используют разделяемый ресурс – строку: каждая нить определяет
текущую длину строки и добавляет код символа с возрастающим значением в
её конец.
Решаемая проблема: т.к. при одновременной работе нитей возможно
«вклинивание» одной нити в работу другой (что приведёт к некорректной
модификации строки), то необходимо реализовать критические участки кода в
которых осуществляется работа с разделяемым ресурсом (взаимное
исключение).
8. Простой пример синхронизации нитей (2)
#include <stdio.h>#include <stdlib.h>
#include <pthread.h>
#define N_SIZE 10
/*разделяемый ресурс*/
char str[N_SIZE + 1];
/*объявление и инициализация мьютекса,
состояние «открыт»*/
pthread_mutex_t mutex =
PTHREAD_MUTEX_INITIALIZER;
int main()
{
pthread_t th_id;
int i;
/*инициализация 0 всего массива символов*/
memset(str, 0, N_SIZE + 1);
/*порождение N_SIZE параллельно
работающих нитей, каждая из которых
добавляет символ к разделяемой строке*/
for (i = 0; i<N_SIZE; i++) {
pthread_create(&th_id, NULL, fThread, NULL);
}
/*точка входа в нить */
void* fThread(void * arg) {
/*вход в критическую секцию*/
pthread_mutex_lock(&mutex);
/*добавляем символ к разделяемой строке*/
int iPos = strlen(str);
str[iPos] = 'A' + iPos;
}
/*выход из критической секции*/
pthread_mutex_unlock(&mutex);
return NULL;
}
/*пауза для ожидания завершения всех нитей*/
sleep(20);
printf("%s\n", str); /*печать итоговой строки*/
return 0;
9. Простой пример синхронизации нитей (3)
Рекомендации:1) Запустить программу, реализующую пример, на компьютере.
2) Провести и запустить на компьютере модификации
программы, реализующей пример, в части:
внесения контроля и обработки всех возможных ошибок;
использования функции pthread_mutex_trylock;
реализации в каждой нити задержки перед входом в
критическую секцию (sleep).
10. Читатели и писатели с использованием нитей (1)
Реализация классической задачи читатели-писатели с приоритетом читателейна основе механизма мьютексов:
• алгоритм идентичен классическому;
• дополнительно описана функция main: запрашивает число читателей и
писателей и порождает соответствующее количество нитей чередованием;
• при порождении/завершении любой нити печатается её TID, тип
(читатель/писатель) и сдвиг в секундах относительно нулевой точки.
11. Читатели и писатели с использованием нитей (2)
#include <stdio.h>#include <time.h>
#include <stdlib.h>
#include <pthread.h>
time_t tmstart;
int rc = 0; /*число одновременно работающих читателей*/
/*мьютексы, состояние «открыт"*/
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t db = PTHREAD_MUTEX_INITIALIZER;
/*эмуляция работы– пауза nsec секунд*/
void do_some_work(int nsec) { sleep(nsec); }
/*реализация нити-читателя*/
void* reader(void * arg) {
pthread_mutex_lock(&mutex);
rc++;
if (rc == 1) pthread_mutex_lock(&db);
pthread_mutex_unlock(&mutex);
do_some_work(5);
pthread_mutex_lock(&mutex);
rc--;
if (rc == 0) pthread_mutex_unlock(&db);
pthread_mutex_unlock(&mutex);
printf("---Читатель %#x завершился на %d секунде\n",
pthread_self(), time(0) - tmstart);
return NULL;
}
void* writer(void * arg) {/*реализация нити писателя*/
pthread_mutex_lock(&db);
do_some_work(12);
pthread_mutex_unlock(&db);
printf("---Писатель %#x завершился на %d секунде\n",
pthread_self(), time(0) - tmstart);
return NULL;
}
int main() {
int nr, nw, bFlag;
pthread_t th_id;
printf(">>Введите число читателей и писателей:\n");
scanf("%u %u", &nr, &nw);
tmstart = time(0); /*точка отсчёта времени в секундах*/
bFlag = !(nr>0);
/*порождение читателей и писателей*/
while (nr>0 || nw>0) {
pthread_create(&th_id, NULL, bFlag ?
writer : reader, NULL );
printf("+++%s %#x запущен на %d секунде\n",
bFlag?"Писатель":"Читатель", th_id, time(0)-tmstart);
/*определяем тип нити для запуска на след.
итерации чередованием, если не исчерпаны*/
bFlag = bFlag ? (nw--, nr == 0) : (nr--, nw);
}
for (;;) sleep(5); /*бесконечный цикл ожидания*/
return 0;
}
12. Читатели и писатели с использованием нитей (3)
Рекомендации:1)Запустить программу, реализующую пример, на компьютере.
2)Провести и запустить на компьютере модификации
программы, реализующей пример, в части:
внесения контроля и обработки всех возможных ошибок;
завершения программы после завершения всех нитей;
модифицировать программу для обеспечения приоритета
нитей-писателей.
3) Реализация программы Читатели и писатели с мьютексами
идентична классической реализации с использованием
семафоров. Т.е. де-факто места использования семафором
заменены на аналогичное использование мьютексов. Вопрос,
можно ли таким же образом реализовать программу Обедающие
философы
с
использованием
мьютексов?
Ответ
прокомментировать с обоснованием.