|
Программирование >> Структура ядра и системные вызовы
~glob dat() { if (mutex destroy(slockx)) perror( mutex destroy ); ); /* установить ноЬое значение */ gl ob dat& operator=( int n€w val ) if (!mutex lock(slockx)) { val = new val; if (mutex unlOck(slockx)) perror( mutex unlock ); )else perror( mutex lock ); return *tliis; /* выбрать значение */ int getval( int* valp ) if (!mutex lock(Slockx)) { *valp= val; if (!mutex urilock(Slockx)> return 0; perror( mutex unlock ); ) else perror ( mutex lock ) ; return -1; /* направить значение в поток вывода */ friend ostreams operator ( ostreams os, glob dats gx) If (!mutex lock(sgx.lockx)) { OS gx.val; if (mutex unlock(sgx.lockx)) perror( mutex lock ); ) else return os; perror( mutex lock ); fendif /* glob dat */ Значение переменной типа glob dat сохраняется в поле glob dat::val. Взаимоисключающая блокировка glob dat::lockx используется для организации доступа к glob dat::val для нескольких процессов. Члены glob dat::lockx и glob dat::val инициализируются при определении переменной этого типа. Блокировка glob dat::lockx удаляется при вызове функции-деструктора glob dat:: ~glob dat. Функции-члены glob dat::getval, glob dat::show и glob dat::operator= позволяют потокам выбирать, показывать и устанавливать любое значение переменной типа globjdat. Благодаря наличию взаимоисключающей блокировки эти функции могут одновременно вызываться несколькими потоками для работы с одной и той же переменной. Приведенный ниже пример (прогр&миа glob dat.Q показывает, как несколько потоков обращаются к переменной тгаа globjdat и globx: ♦include <strstream.h> . ♦include <tliread.h> , ♦include glob dat.h ;i glob dat globx (0); void* mod val (void* np ) ( определить globx int old val=0, new val; ,/ istrstream((cliar*) np) new vaij., if (!globx.getval(sold val)) { получить текущее значение globx = old val + newval; добавить новое значение cout (int)thr selfО : arg= (char*)np -> globx endl; показать результат return 0; int main( int argc, char*argv[]) thread t tid; while (--argc > 0) для каждого аргумента командной строки if (thr create(О,O,mod val,argv[argc], О, stid)) perror( thr create ); while (thr join(0,0,0)) ; ожидать завершения потоков return 0; Функция main создает переменную globx с нулевым начальным значением. Затем для каждого аргумента командной строки она создает поток выполнения функции mod val. С каждым вызовом modval в качестве аргумента передается аргумент командной строки. Он состоит из текстовой строки целочисленного значения (например, 51 ). Функция mo£( va/преобразует свой аргумент в целочисленное значение, получает текущее значение globx и добавляет новое целочисленное значение к текущему. Результат присваивается переменной globx, значение которой затем отображается на стандартном устройстве вывода. Ниже прргееден пример запуска этой программы и полученные результаты: % СС glob dat.C -Ithread -о glob dat % glob dat 1 2 3 4: arg=3 -> 3 5: arg=2 -> 5 6: arg=l -> 6 При использовании взаимоисключающих блокировок следует избегать создания тупиковых ситуаций. Такая ситуация может возникнуть, когда процесс использует несколько взаимоисключающих блокировок и два или более потоков в этом процессе пытаются стать владельцами этих блокировок в произвольной последовательности. Пусть, например, в процессе инициализированы две взаимоисключающие блокировки (А и В), которыми пользуются два потока (X и Y). Допустим, поток X установил блокировку А, а поток Y - блокировку В. Когда X пытается стать владельцем блокировки В, он блокируется, потому что эта блокировка установлена потоком Y. Если затем поток Y пытается стать владельцем блокировки А, он тоже блокируется. Таким образом создается тупиковая ситуация: блокируются оба потока, потому что каждый из них пытается установить блокировку, принадлежащую другому потоку. Ни один из этих потоков не будет выполняться, потому что владелец блокировки заблокирован и не может снять ее. Во избежание таких ситуаций потоки, пользующиеся взаимоисключающими блокировками, должны всегда устанавливать их в одном и том же порядке и/или пользоваться для установки блокировок функцией thrjtrywait, а не функцией thr wait. В первом случае все потоки устанавливают блокировки в одном и том же порядке, поэтому поток не может попытаться завладеть блокировкой, принадлежащей заблокированному потоку. Если же поток устанавливает блокировку с помощью функции thrjrywait, то она сразу же возвращает ненулевое значение, показывающее, что блокировка принадлежит другому потоку. Поток, таким образом, не блокируется, может выполнять другие операции и повторить попытку установки блокировки позже, 13.5.2. Условные переменные Условные переменные используются для того, чтобы заблокировать потоки до выполнения определенных условий. Условные переменные обычно применяются в сочетании со взаимоисключающими блокировками, чтобы несколько потоков могли ожидать момента выполнения одного условия. Это можно сделать следующим образом. Сначала поток устанавливает взаимоисключающую блокировку, но и сам блокируется до момента выполнения определенного условия. На то время, пока поток заблокирован, установленная им блокировка автоматически снимается. Когда другой поток выполняет поставленное условие, он дает условной переменной сигнал о разблокировании первого потока. После разблокирования потока взаимоисключающая блокировка автоматически вновь устанавливается, и первый поток повторно проверяет условие. Если оно не выполняется, поток опять блокируется условной переменной. Если же условие выполняется, поток снимает взаимоисключающую блокировку и выполняется дальше. 13.5.2.1. Условные переменные фирмы Sun Ниже перечислены многопотоковые библиотечные функции фирмы Sun для управления условными переменными. Функция Назначение cond init Инициализирует условную переменную cond wait Блокирует по условной переменной cond timedwait То же, что cond wait, но указывается тайм-аут блокировки cond signal Разблокирует поток, ожидающий условную переменную cond broadcast Разблокирует все потоки, ожидающие условную переменную cond destroy Удаляет условную переменную Прототипы этих функций выглядят следующим образом: #include <thread.h> int condjnit (cond t* condp, int type, int argp); int cond wait (cond t* condp, mutex t* mutxp,); int condjtimedwait (cond t* condp, mutex t* mutxp, timestruct t* timp); int condjsignal (con6 \* condp); ~ int cond broadcast (сопй \* condp); int cond destroy (cond t* condp); Значение аргумента condp - адрес переменной типа condj. Эта переменная устанавливается как ссылка на условную переменную посредством функции condjnit. Аргумент type функции condjnit показывает, доступна ли данная условная переменная для других процессов. Приведем его возможные значения.
Аргумент arg функции condjnit в настоящее время не задействуется. Его значение должно быть равно 0. Функция cond wait блокирует вызывающий поток, заставляя его ожвдать изменения условной переменной в соответствии с аргументом condp. Она также снимает взаимоисключающую блокировку, зацанную аргументом mutxp. Когда другой поток вызывает функцию cond signal по той же условной переменной, взаимоисключающая блокировка вновь устанавливается ожидающим потоком. Сам этот поток разблокируется и возобновляет выполнение после возврата из функции cond wait. Если эта условная переменная заблокировала несколько потоков, все они должны использовать одну и ту же взаимоисключающую блокировку. Когда условная переменная изменяет состояние, установить взаимоисключающую блокировку и продолжать выполнение может только один из заблокированных потоков. Остальные потоки остаются заблокированными этой же блокировкой. Функция condjimewait похожа на функцию cond wait, только в ней есть третий аргумент, timp, который показывает, что вызывающий поток разблокируется по истечении времени, указанного в этом аргументе. Функция condjsignal изменяет состояние условной переменной, заданной аргументом condp, и разблокирует ожидающий эту переменную поток. Функция cond broadcast изменяет состояние условной переменной и разблокирует все ожидающие потоки. Если данную условную переменную не ожидает ни один поток, то вызовы cond signal и cond broadcast с этой переменной не влекут никаких последствий. 13.5.2.2. Пример программы с условными переменными Приведенная ниже программа pipe. С иллюстрирует использование взаимоисключающей блокировки и условной переменной. Эта программа создает поток чтения и поток записи. Поток чтения читает данные, вводимые пользователем, и передает их в поток записи через глобальный массив msgbuf. Поток записи направляет сообщения, содержащиеся в msgbuf, на стандартное устройство вывода. Эти два потока завершаются, когда во входных данных встречается признак конца файла. Доступ потоков чтения и записи к массиву msgbuf синхронизируется с помощью взаимоисключающей блокировки и условной переменной. Вот текст программы pipe. С: tinclude <iostream.h> tinclude <thread.h> tinclude <string.h>>*~ tinclude <stdio.h> i ., tinclude <signal. h> tdefine FINISH() { cerr (int)thr self0 exitsXn ; \ mutex unlock(&mutx); thr exit(0); return 0; } mutex t mutx; cond t condx; int msglen, done; char msgbuf[256]; направить сообщения, посылаемые из потока чтения, на стандартное устройство вывода */ void* writer (void* argp ) { do { --С . mutex lock(smutx); установить взаимоисключающую блокировку while (!msglen) ( цикл, если сообщения нет cond wait(Scondx, smutx); ждать условную переменную if (done) FINISH О; если выполнено, уничтожить поток cout *> msgbuf endl; /* направить сообщение на стандартное устройство вывода */ msglen 0; размер буфера сообщений mutex unlock(Smutx); снять взаимоисключающую блокировку ) while (1); FINISH О; заверщение и выход /* читать сообщения пользователя и посылать их в поток записи */ void* reader (void* argp ) do ( mutex lock(smutx); установить взаимоисключающую блокировку if (!msglen) ( проверить, пуст ли буфер if (!cin.getline(msgbuf,256)) break; /* получить входные данные от пользователя */ if (cin.eofO) break; msglen = strlen(msgbuf)+1; установить длину сообщения cond signal(Scondx); /* дать потоку записи сигнал для чтения сообщения */ else thr yield(); i1[iutex unlock(Smutx) ? снять взаимоисключающую блокировку while (1); FINISH О; завершение и выход /* главный поток для управления потоком чтения и потоком записи */ main О { thread t wtid, rtid, tid; /* инициализировать взаимоисключающую блокировку и условную переменную */ mutex init(Smutx, USYNC PROCESS, 0); cond init(Scondx, USYNC PROCESS, 0); /* создать поток записи */ if (thr create(0,0,writer,0,0,Swtid)) perror( thr create ) ; /* создать поток чтения */ if {thr create(0,О,reader,О,О,srtid)) perror( thr create );
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |