Основы многопоточного и распределенного программирования

       

Учебные примеры: библиотека Pthreads


Напомним, что поток — это "облегченный" процесс, т.е. процесс с собственным про­граммным счетчиком и стеком выполнения, но без "тяжелого" контекста (типа таблиц стра­ниц), связанного с работой приложения. Некоторые операционные системы уже давно пре­доставляли механизмы, позволявшие программистам писать многопоточные приложения. Но эти механизмы отличались, поэтому приложения нельзя было переносить между разными операционными системами или даже между разными типами одной операционной системы. Чтобы исправить эту ситуацию, большая группа людей в середине 1990-х годов определила стандартный набор функций языка С для многопоточного программирования. Эта группа ра­ботала под покровительством организации POSIX (Portable Operating System Interface — ин­терфейс переносимой операционной системы), поэтому библиотека называется Pthreads для потоков POSIX. Сейчас эта библиотека широко распространена и доступна на самых разных типах операционных систем UNIX и некоторых других системах

Библиотека Pthreads содержит много функций для управления потоками и их синхрони­зации. Здесь описан только базовый набор функций, достаточный для создания и соединения потоков, а также для синхронизации работы потоков с помощью семафоров. (В разделе 5.5 опи­саны функции для блокировки и переменных условия.) Также представлен простой, но доста­точно полный пример приложения типа "производитель-потребитель". Он может служить базо­вым шаблоном для других приложений, в которых используется библиотека Pthreads.

Глава 4. Семафоры                                                                                                                         155

нием области планирования. Часто программист хочет, чтобы планирование потока про­исходило глобально, а не локально, т.е. чтобы поток конкурировал за процессор со всеми потоками, а не только с созданными его родительским потоком. В вызове функции pthread_attr_setscope, приведенном выше, это учтено.10 Новый поток создается вызовом функции pthread_create:


pthread_create(&tid,   &tattr,   start_func,   arg);

Первый аргумент — это адрес дескриптора потока, заполняемый при его успешном создании. Второй — адрес дескриптора атрибутов потока, который был инициализирован предыдущим. Новый поток начинает работу вызовом функции start_func с единственным аргументом arg. Если поток создан успешно, функция pthread_create возвращает нуль. Другие зна­чения указывают на ошибку.

Поток завершает свою работу следующим вызовом: pthread_exit(value);

Параметр value — это скалярное возвращаемое значение (или NULL). Процедура exit вы­зывается неявно, если поток возвращается из функции, выполнение которой он начал.

Родительский процесс может ждать завершения работы сыновнего процесса, выполняя функцию

pthread_join(tid,   value_ptr);

Здесь tid является дескриптором сыновнего процесса, а параметр value_ptr — адресом пе­ременной для возвращаемого значения, которая заполняется, когда сыновний процесс вызы­вает функцию exit.

4.6.2. Семафоры

Потоки взаимодействуют с помощью переменных, объявленных глобальными по отно­шению к функциям, выполняемым потоками. Потоки могут синхронизироваться с помощью активного ожидания, блокировок, семафоров или условных переменных. Здесь описаны се­мафоры; блокировки и мониторы представлены в разделе 5.5.

Заголовочный файл semaphore.h содержит определения и прототипы операций для се­мафоров. Дескрипторы семафоров определены как глобальные относительно потоков, кото­рые будут их использовать, например: sem_t mutex;



Дескриптор инициализируется вызовом функции sem_init. Например, следующий вызов присваивает семафору mutex значение 1:

sem_init(&mutex,   SHARED,   1) ;

Если параметр shared не равен нулю, то семафор может быть разделяемым между процесса­ми, иначе семафор могут использовать только потоки одного процесса. Эквивалент операции Р в библиотеке Pthreads— функция sem_wait, а операции V— функция sem_post. Итак, один из способов защиты критической секции с помощью семафоров выглядит следующим образом.



sem_wait(umutex);       /*  Р(mutex)   */

критическая секция;

sem_post(fcmutex);       /* V(mutex)   */

Кроме того, в библиотеке Pthreads есть функции для условного ожидания на семафоре, полу­чения текущего значения семафора и его уничтожения.

10

Программы, приведенные в книге и использующие библиотеку Pthreads, тестировались с помощью операционной системы Solaris. На других системах для некоторых атрибутов могут понадобиться другие значения. Например, в системе IRIX видимость для планирования должна быть указана как PTHREAD_SCOPE_PROCESS, и это значение установлено по умолчанию.

156                                               Часть 1 Программирование с разделяемыми переменными

4.6.3. Пример: простая программа типа "производитель-потребитель"

В листинге 4.14 приведена простая программа типа производитель-потребитель, анало­гичная программе в листинге 4.3. Функции Producer и Consumer выполняются как незави­симые потоки. Они разделяют доступ к буферу data. Функция Producer помещает в буфер последовательность целых чисел от 1 до значения numlters. Функция Consumer извлекает и складывает эти значения. Для обеспечения попеременного доступа к буферу процесса-производителя и процесса-потребителя использованы два семафора, empty и full.

Функция main инициализирует дескрипторы и семафоры, создает два потока и ожидает завершения их работы. При завершении потоки неявно вызывают функцию pthread_exit. В данной программе аргументы потокам не передаются, поэтому в функции pthread_create использован адрес NULL. Пример, в котором используются аргументы по­токов, приведен в разделе 5.5.



Глава 4 Семафоры

}

printf{"Сумма равна  %d\n",   total);

}___________________________________________________________________


Содержание раздела