Лабораторная работа №5
по курсу лекций “Операционные системы”
Сорокин Н.Ю., 2004г.
Синхронизация потоков
Цель работы
Ознакомиться с функциями, предназначенными для синхронизации потоков в ОС Linux. Научиться использовать мьютексы и условные переменные для решения задач доступа к ресурсам.
Общие сведения
При выполнении нескольких потоков довольно часто требуется их синхронизировать в таких ситуациях, как осуществление доступа к общем ресурсам. Одно из основных преимуществ использования потоков – это легкость использования средств синхронизации. В лабораторной мы рассмотрим следующие средства синхронизации, используемые в ОС Linux:
· Блокировки взаимного исключения (мьютекс)
· Условные переменные
Для понимания значения синхронизации рассмотрим простой пример. Предположим что два потока используют одну и ту же глобальную переменную.
Поток А |
Поток В |
x = common_variable ; |
y = common_variable ; |
x++ ; |
y-- ; |
common_variable = x ; |
common_variable = y ; |
В данном случае результат будет зависеть от того, в какой последовательности будет происходить выполнение двух процессов. Данная ситуация называется состязанием. Состязания обычно трудно обнаружить, потому что они появляются время от времени без какой-либо закономерности.
Мьютексы
Мьютекс (mutex) – это сокращение от «взаимное исключение» (mutual exlusion). Мьютексы – это один из основных способов реализации синхронизации потоков в ОС Linux.
Мьютекс действует как блокировка, защищающая доступ к разделенной структуре данных. Основная идея работы мьютексов заключается в том, что в определенный момент времени захватить мьютекс может только один единственный поток. Никакие потоки не могут захватить мьютекс до тех пор, пока владеющий поток не освободит данный мьютекс.
Очень часто потоки, использующие мьютексы, выполняют действия над глобальными переменными. Использование мьютексов – безопасный способ изменения глобальной переменной несколькими потоками, гарантирующий, что конечное значение будет таким же, как и в случае, если бы эти действия выполнялись одним потоком.
Функция |
int pthread_mutex_init( pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); |
Описание |
Создание и инициализация нового мьютекса mutex и установка его атрибутов в соответствие с атрибутами мьютекса mutexattr. При создании мьютекс разблокирован. Вместо указателя на атрибуты мьютекса допускается передача указателя NULL, что означает атрибуты по умолчанию. |
Функция |
int pthread_mutex_destroy(pthread_mutex_t *mutex); |
Описание |
Уничтожение мьютекса. |
Функции |
int pthread_mutexattr_init(pthread_mutexattr_t *attr); int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); |
Описание |
Создание и уничтожение атрибутов мьютекса. |
Функция |
int pthread_mutex_lock(pthread_mutex_t *mutex); |
Описание |
Захват мьютекса. Если данный мьютекс уже захвачен другим потоком, этот вызов заблокирует вызывающий поток до тех пор, пока мьютекс не будет освобожден. |
Функция |
int pthread_mutex_trylock(pthread_mutex_t *mutex); |
Описание |
Попытка захватить мьютекс. Однако если мьютекс уже захвачен другим потоком, этот вызов не будет блокировать вызывающий поток, а вернет управление немедленно. Эта функция может быть полезна для предотвращения взаимоблокировок. |
Функция |
int pthread_mutex_unlock(pthread_mutex_t *mutex); |
Описание |
Освобождение мьютекса, если его владелец – вызывающий поток. Вызов этой функции необходим, если поток закончил работу с защищенными данными и другие потоки ожидают освобождения мьютекса. Если мьютекс уже освобожден, или владельцем его является другой поток, то функция возвращает ошибку. |
Примеры использования мьютексов
Пример 1. Программа без мьютексов.
#include <pthread.h>
#include <stdio.h>
int x=1;
void* compute_thread(void * argument) {
printf("X value in thread before sleep = %d\n",x);
printf("X value in thread is incremented by 1 before sleep\n");
x++;
sleep(2);
printf("X value in thread after sleep = %d\n",x);
return;
}
main( )
{
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&tid, &attr, compute_thread,(void *)NULL);
sleep(1);
x++;
printf("Main thread increments X, after that X value is %d\n",x);
pthread_join(tid,NULL);
exit(0);
}
Пример 2. Предыдущий пример, но с использованием мьютекса.
#include <pthread.h>
#include <stdio.h>
int x=1;
/* This is the lock for thread synchronization */
pthread_mutex_t my_sync;
void* compute_thread(void * argument) {
printf("X value in thread before sleep = %d\n",x);
printf("X value in thread is incremented by 1 before sleep\n");
pthread_mutex_lock(&my_sync);
x++;
sleep(2);
printf("X value in thread after sleep = %d\n",x);
pthread_mutex_unlock(&my_sync);
return;
}
main( ) {
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
/* Initialize the mutex (default attributes) */
pthread_mutex_init (&my_sync,NULL);
pthread_create(&tid, &attr, compute_thread,(void *)NULL);
sleep(1);
pthread_mutex_lock(&my_sync);
x++;
printf("Main thread increments X, after that X value is %d\n",x);
pthread_mutex_unlock(&my_sync);
pthread_join(tid,NULL);
exit(0);
}
Условные переменные
Условные переменные предлагают еще один способ синхронизации потоков. В то время как мьютексы реализуют синхронизацию путем контроля доступа со стороны потоков к данным, условные переменные позволяют потокам синхронизироваться, исходя из значения переменной.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.