Смекни!
smekni.com

Учебно-методическое пособие рекомендовано учебно-методическим советом Международного университета природы, общества и человека (стр. 11 из 11)

Семафоры – не единственное средство синхронизации потоков. Для разграничения доступа к глобальным объектам потоки могут использовать мьютексы. Все функции и типы данных, имеющие отношение к мьютексам, определены в файле pthread.h. Мьютекс создается вызовом функции pthread_mutex_init(3). В качестве первого аргумента этой функции передается указатель на переменную pthread_mutex_t, которая играет роль идентификатора нового мьютекса. Вторым аргументом функции pthread_mutex_init() должен быть указатель на переменную типа pthread_mutexattr_t. Эта переменная позволяет установить дополнительные атрибуты мьютекса. Если нам нужен обычный мьютекс, мы можем передать во втором параметре значение NULL. Для того чтобы получить исключительный доступ к некоему глобальному ресурсу, поток вызывает функцию pthread_mutex_lock(3), (в этом случае говорят, что «поток захватывает мьютекс»). Единственным параметром функции pthread_mutex_lock() должен быть идентификатор мьютекса. Закончив работу с глобальным ресурсом, поток высвобождает мьютекс с помощью функции pthread_mutex_unlock(3), которой также передается идентификатор мьютекса. Если поток вызовет функцию pthread_mutex_lock() для мьютекса, уже захваченного другим потоком, эта функция не вернет управление до тех пор, пока другой поток не высвободит мьютекс с помощью вызова pthread_mutex_unlock() (после этого мьютекс, естественно, перейдет во владение нового потока). Удаление мьютекса выполняется с помощью функции pthread_mutex_destroy(3). Стоит отметить, что в отличие от многих других функций, приостанавливающих работу потока, вызов pthread_mutex_lock() не является точкой останова. Иначе говоря, поток, находящийся в режиме отложенного досрочного завершения, не может быть завершен в тот момент, когда он ожидает выхода из pthread_mutex_lock().

Практическая часть

Пример 1

Вернемся к примеру 1 из предыдущего практического занятия. Напомню, что в том примере мы создавали два потока, используя одну и ту же функцию потока. В процессе создания каждого потока этой функции передавалось целочисленное значение (номер потока). При этом для передачи значения каждому потоку использовалась своя переменная. В тот момент объяснялось почему мы не можем использовать одну переменную для передачи значения. Проблема заключалась в том, что мы не могли знать, когда именно новый поток начнет выполняться. С помощью средств синхронизации потоков мы можем решить эту проблему и использовать одну переменную для передачи значений обоим потокам.

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <pthread.h>

#include <semaphore.h>

sem_t sem;

void * thread_func(void * arg)

{

int i;

int loc_id = * (int *) arg;

sem_post(&sem);

for (i = 0; i < 4; i++) {

printf("Thread %i is running&bsol;n", loc_id);

sleep(1);

}

}

int main(int argc, char * argv[])

{

int id, result;

pthread_t thread1, thread2;

id = 1;

sem_init(&sem, 0, 0);

result = pthread_create(&thread1, NULL, thread_func, &id);

if (result != 0) {

perror("Creating the first thread");

return EXIT_FAILURE;

}

sem_wait(&sem);

id = 2;

result = pthread_create(&thread2, NULL, thread_func, &id);

if (result != 0) {

perror("Creating the first thread");

return EXIT_FAILURE;

}

result = pthread_join(thread1, NULL);

if (result != 0) {

perror("Joining the first thread");

return EXIT_FAILURE;

}

result = pthread_join(thread2, NULL);

if (result != 0) {

perror("Joining the first thread");

return EXIT_FAILURE;

}

sem_destroy(&sem);

printf("Done&bsol;n");

return EXIT_SUCCESS;

}

Присвоив семафору значение 0, программа создает первый поток и вызывает функцию sem_wait(). Эта функция приостановит выполнение функции main() до тех пор, пока функция потока не вызовет функцию sem_post(), а это случится только после того как функция потока обработает значение переменной id. Таким образом, мы можем быть уверены, что в момент создания второго потока первый поток уже закончит работу с переменной id, и мы сможем использовать эту переменную для передачи данных второму потоку.

Варианты заданий

Перевести приложение, разработанное в теме №2 на платформу ОС Linux. Для задачи синхронизации примените семафоры или мьютексы (по вашему выбору).