Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Text111

.pdf
Скачиваний:
49
Добавлен:
06.02.2018
Размер:
1.16 Mб
Скачать

}

}

void procthread2(void *arg) {int k, j;

for (k=0; k<24; k++) {printf("\033[%02d;40H",k+1); printf("\033[1;32m");

for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]); if (k= =5) {pthread_cancel(tid3);

printf("-Terminate thread3");} printf("\n");

usleep(1300000);

}

}

void procthread3(void *arg) {int k, j;

// pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

/// pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); for (k=0; k<24; k++)

{printf("\033[%02d;60H",k+1);

printf("\033[1;31m"); for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]);

printf("\n");

usleep(1100000);

}

}

int main() {int k; int rc;

printf("\033[2J\n");

rc=pthread_create(&tid1, NULL, (void*)procthread1, (void*)2); rc=pthread_create(&tid2, NULL, (void*)procthread2, (void*)3); rc=pthread_create(&tid3, NULL, (void*)procthread3, (void*)4);

for (k=0; k<24; k++) {printf("\033[%02d;1H",k+1);

printf("\033[1;37m");

printf("%c++",lbuk[k]);

printf("\n");

usleep(1000000);

}

 

pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); printf("\033[0m");

return 0;

}

Листинг 8.3.1 Уничтожение нити по явному приказу другой нити в Linux

Вэтой программе вторая нить, реализующая действия процедуры procthread2 на пятом шаге (при k=5), отдает приказ на уничтожение третьей нити (нити с процедурой procthread3).

Если убрать символы комментариев перед первым оператором третьей нити, то вызов функции pthread_setcancelstate() в этом операторе будет переключать эту нить в состояние неотменяемости, и при запуске программы можно наблюдать, что приказ на уничтожение нити в действительности не выполняется. Попытки же перевести указанную нить в состояние синхронной отменяемости не имеют явного эффекта из-за того, что операторы printf вывода на экран неявно создают точки отмены, на которых и происходит прекращение работы этой нити по приказу на ее уничтожение.

Воперационных системах Windows для уничтожения процессов служит системная функция TerminateThread, которая имеет прототип

BOOL TerminateThread(HANDLE hThread, DWORD dwExitCode).

Код возврата из завершившейся или принудительно прекращенной приказом TerminateThread нити может быть получен в другой нити того же процесса путем вызова вспомогательной функции GetExitCodeThread, которая имеет прототип

BOOL GetExitCodeThread(HANDLE hThread, DWORD *pExitCode).

Если задача, указанная аргументом hThread, в момент запроса кода возврата еще работает, то функция возвращает значение STILL_ACTIVE в качестве значения второго аргумента. Код возврата нормально завершающейся нити формирует функция завершения ExitThread, которая имеет прототип

void ExitThread(DWORD dwExitCode).

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

Аналогично рассмотренным выше примерам, функцию уничтожения нити в операционных системах Windows можно продемонстрировать изменением программы из листинга 8.2.2, вставив оператор

if (k= =5) TerminateThread(hthread3, 0) после операторов

for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]); в процедуре procthread2.

8.4. Приостановка и повторный запуск нити

Для нитей существует один вид системного действия, аналогов которому нет для процессов в операционных системах Windows и OS/2. Это – приостановка и повторный запуск отдельной нити. По смыслу действия одна из нитей процесса отдает приказ на приостановку другой нити этого же процесса. Приостановить нить другого процесса программным приказом невозможно (так как в подобном действии при внимательном анализе нет никакого практического смысла).

В операционной системе Unix для приостановки отдельной нити можно и нужно использовать уже знакомую читателю функцию pthread_kill со вторым параметром, задаваемым символической константой SIGSTOP, а для возобновления работы нити - ту же функцию, но уже с параметром SIGCONT. Следующий пример, представленный программой в листинге 8.4.1, демонстрирует приостановки и возобновление работы отдельной нити в операционной системе Unix, поддерживающей стандарт POSIX.

#include <pthread.h> #include <stdio.h> #include <signal.h>

char lbuk[ ]="abcdefghijklmnoprstuwxyz"; pthread_t tid1, tid2, tid3;

void procthread1(void *arg) {int k, j;

for (k=0; k<24; k++) {printf("\033[%02d;20H",k+1); printf("\033[1;34m");

for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]);

if (k= =5) {pthread_kill(tid2, SIGSTOP); printf("-Suspend thread2");}

if (k= =11) {pthread_kill(tid2, SIGCONT); printf("-Resume thread2");}

printf("\n"); usleep(800000);

}

}

void procthread2(void *arg) {int k, j;

for (k=0; k<24; k++) {printf("\033[%02d;40H",k+1); printf("\033[1;32m");

for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]);

usleep(1300000);

}

}

void procthread3(void *arg) {int k, j;

for (k=0; k<24; k++) {printf("\033[%02d;60H",k+1); printf("\033[1;31m");

for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]);

printf("\n");

usleep(1100000);

}

}

int main() {int k; int rc;

printf("\033[2J\n");

rc=pthread_create(&tid1, NULL, (void*)procthread1, (void*)2); rc=pthread_create(&tid2, NULL, (void*)procthread2, (void*)3); rc=pthread_create(&tid3, NULL, (void*)procthread3, (void*)4); for (k=0; k<24; k++)

{printf("\033[%02d;1H",k+1); printf("\033[1;37m"); printf("%c++",lbuk[k]);

printf("\n");

usleep(1000000);

}

 

pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); printf("\033[0m");

return 0;

}

Листинг 8.4.1. Программа с приостановкой нитей для Unix

В этой программе первая нить на 5-м шаге своей работы отдает приказ приостановки второй нити (идентификатор которой в tid2). Второй нитью мы здесь называем нить с идентификатором tid2, работающую на основе процедуры procthread2, а главную нить условно считаем нулевой. Приказ такой приостановки выдается в виде

pthread_kill(tid2, SIGSTOP).

Сама первая нить после такого приказа продолжает работать, а вторая, как легко наблюдать на экране по выводимым результатам, временно ничего не выводит, будучи приостановленной. Далее на 11-м шаге работы первой нити – в процедуре procthread1 вызывается функция возобновления работы приостановленной второй нити. Эта функция в программе присутствует в виде pthread_kill(tid2, SIGCONT). После ее выполнения, как можно наблюдать по выводу процесса, который работает по программе из листинга 8.4.1, вторая нить продолжает свой вывод, а следовательно, работает дальше. Небольшие дополнения в программе относительно ее аналога в листинге 8.2.1 осуществляют поясняющий вывод в ходе ее выполнения и предназначены для наглядности наблюдаемого процесса.

Программные средства для приостановки и возобновления нитей в операционных системах типа Windows состоят из двух функций ResumeThread и SuspendThread. Обе они имеют единственный аргумент – хэндл нити, на которую они должны подействовать – приостановить или возобновить ее работу. Прототипы этих функций описываются в виде:

DWORD SuspendThread(HANDLE hthread); DWORD ResumeThread(HANDLE hthread).

Использование этих функций не имеет никаких особенностей в сравнении с другими рассмотренными операционными системами. В листинге 8.4.2 приведены фрагменты программы, отсутствующие части которой должны быть взяты из листинга 8.2.2 (начальная часть, тексты процедур procthread1, procthread2 и опущенные детали остальных). Эта программа создает три нити, кроме главной, и в ходе их работы в одной из них (третьей по условному счету) приостанавливает работу первой из них. Затем через несколько шагов внутреннего цикла в процедуре третьей нити отдается приказ на возобновление работы приостановленной ранее нити.

#include <windows.h> #include <process.h>

. . .

DWORD WINAPI procthread3(void *arg) {int k, j;

COORD pos;

for (k=0; k<24; k++) {

. . .

for (j=0; j<(int)arg; j++) printf("%c",lbuk[k]); if (k==5)

{SuspendThread(hthread1);

pos.X=50; pos.Y=k+1; SetConsoleCursorPosition(hstdout,pos); printf("Suspend thread1 into step=5");

}

if (k==16) {ResumeThread(hthread1);

pos.X=50; pos.Y=k+1; SetConsoleCursorPosition(hstdout,pos); printf("Resume thread1 into step=16");

}

. . .

Sleep(1000);

}

}

void main()

{unsigned long threadid1, threadid2, threadid3; int k;

COORD pos;

hstdout=GetStdHandle(STD_OUTPUT_HANDLE);

. . .

 

for (k=0; k<24; k++)

{

. . .

 

printf("%c++",lbuk[k]); if (k==9)

{TerminateThread(hthread2, 0);

pos.X=1; pos.Y=k+1; SetConsoleCursorPosition(hstdout,pos); printf("Kill thread2");

}

LeaveCriticalSection(&csec);

Sleep(1500);

}

. . .

CloseHandle(hthread1); CloseHandle(hthread2); CloseHandle(hthread3);

}

Листинг 8.4.2. Фрагменты программа с уничтожением нитей для Windows

Заметим, что в программе для управления поведением нитей используются их хэндлы, но никак не используются идентификаторы нитей. Практически в Windows всегда заметна некоторая избыточность средств, которая частично оправдывается большой суммарной сложностью системы.

8.5. Ожидание завершения нити

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

В операционной системе OS/2 функция ожидания завершения нити называется DosWaitThread и имеет прототип

APIRET DosWaitThread(PTID ptid, ULONG option).

Эта функция в данной операционной системе предназначена для целого спектра действий. Она может быть использована для ожидания завершения конкретной нити того же процесса, заданной идентификатором в первом аргументе. (Отметим, что этот аргумент – возвращаемый!) При этом второй аргумент должен быть задан символической константой DCWW_WAIT или просто ее значением, равным нулю. Она может быть использована для получения информации, завершена ли конкретная нить данного процесса. Тогда второй аргумент должен задаваться константой DCWW_NOWAIT, равной 1. Результаты выполнения функции DosWaitThread при этом выдаются через код возврата. Именно, если указанная в вызове нить завершена, возвращается нулевой код, если не завершена, возвращается код ХХХХ, представляемый символической константой ХХХ_ХХХХХ.

Эта же функция DosWaitThread может быть использована для приостановки выполняющей ее нити до тех пор, пока какая-то другая нить этого же процесса не будет завершена. Для этих целей функция DosWaitThread должна быть вызвана с нулевым значением переменной для идентификатора нити, на которую указывает первый аргумент функции. С этой целью можно использовать последовательность операторов

tid=0; DosWaitThread(&tid, DCWW_WAIT),

где переменная tid определена как имеющая тип TID. В результате такого выполнения функции DosWaitThread будет получено значение идентификатора (в переменной tid – для указанного примера), обозначающего ту нить, которая была завершена после вызова этой функции.

Наконец, эта же функция может быть использована без ожидания для получения информации, какая нить в данном процессе была завершена последней. В этом применении вызов функции должен иметь второй аргумент, равный DCWW_NOWAIT, а первый аргумент быть указателем на нулевое значение переменной для идентификатора нити.

В операционной системе Unix, поддерживающей стандарт POSIX, для ожидания нити служит функция pthread_join, имеющая прототип

int pthread_join(pthread_t tid, void** status).

Эта функция возвращает код возврата из нити, переданный через функцию pthread_exit(), завершающую нить. Функция пригодна для ожидания только конкретной нити, идентификатор которой задается первым ее аргументом. Второй аргумент вызова функции (указатель на указатель) может быть использован в виде последовательности

тип_результата restatus; void *status=&restatus;

. . .

pthread_exit(&restatus); // в конце процедуры нити

. . .

pthread_join(tid, &status); <анализ значения *status>

либо в простейшем случае может быть использовано явное преобразование типа.

#include <stdio.h>

char lbuk[ ]="abcdefghijklmnoprstvuwxy"; pthread_t tid1, tid2, tid3;

void procthread1(void *arg)

 

 

{int k, j;

 

 

 

for (k=0; k<24; k++)

{

 

 

// установить вывод в позицию (20,k+1) и синий цвет

printf("\033[%02d;20H",k+1);

printf("\033[1;34m");

for (j=0; j<(int)arg; j++)

printf("%c",lbuk[k]);

printf("\n");

 

 

 

usleep(1000000);

 

 

 

}

 

 

 

}

 

 

 

void procthread2(void *arg)

 

 

{int k, j;

 

 

 

for (k=0; k<24; k++)

{

 

 

//установить вывод в позицию (40,k+1) и зеленый цвет

printf("\033[%02d;40H",k+1);

printf("\033[1;32m");

for (j=0; j<(int)arg; j++)

printf("%c",lbuk[k]);

printf("\n");

 

 

 

usleep(1000000);

 

 

 

}

 

 

 

}

 

 

 

void procthread3(void *arg)

 

 

{int k, j, *rc;

 

 

 

for (k=0; k<7; k++)

{

 

 

// установить вывод в позицию (60,k+1) и красный цвет

printf("\033[%02d;60H",k+1);

printf("\033[1;31m");

for (j=0; j<(int)arg; j++)

printf("%c",lbuk[k]);

printf("\n");

 

 

 

usleep(1000000);

 

 

 

}

 

 

 

printf("\033[%02d;60H",k+1);

printf("\033[1;31m");

printf("Waiting End thread1\n"); pthread_join(tid1, (void**)&rc);

printf("\033[%02d;60H",k+1); // установить вывод в позицию (60,k+2)

printf("\033[1;31m");

// и красный цвет

printf("End waiting\n");

 

for (k=9; k<24; k++) {

 

printf("\033[%02d;60H",k+1); // установить вывод в поз. (60,k+1)

printf("\033[1;31m");

// и красный цвет

for (j=0; j<(int)arg; j++)

printf("%c",lbuk[k]);

printf("\n");

 

usleep(1000000);

}

}

void main() {int k;

printf("\033[2J\n");//clrscr();

pthread_create(&tid1, NULL, (void*)procthread1, (void*)2); pthread_create(&tid2, NULL, (void*)procthread2, (void*)3); pthread_create(&tid3, NULL, (void*)procthread3, (void*)4); for (k=0; k<24; k++) {

printf("\033[%02d;1H",k+1); //установить вывод в позицию (1,k+1) и белый цвет

printf("\033[1;37m");

printf("%c++\n",lbuk[k]); if (k==9)

{pthread_cancel(tid2); // !!!

printf("\033[%02d;1H",k+1); // установить вывод в позицию (1,k+1) printf("Kill Thread2\n");

}

usleep(1500000);

}

printf("\033[%02d;30H",24); // установить вывод в позицию (30,24) pthread_join(tid1,NULL); pthread_join(tid2,NULL); pthread_join(tid3,NULL); printf("\033[0m"); // вернуть вывод на экран к стандартному цвету

}

Листинг 8.5.1. Программа с ожиданием завершения нитей для Unix

В программе с целью упрощения (ввиду того, что код возврата из процедуры не используется) значение кода завершения, возвращаемое с помощью функции pthread_join, описано как целочисленное (в виде int *rc;), а при вызове функции для второго аргумента используется преобразование типов в виде (void**)&rc. После вызова функции ожидания pthread_join третья нить, обратившаяся к этой функции, приостанавливается до тех пор, пока не завершится указанная в нем нить, т.е. первая нить. Завершение этой первой нити приводит к возобновлению действий приостановленной третьей нити. Для более легкого восприятия содержательных ситуаций программа процесса снабжена выдачей сообщений о приостановке и возобновлении нити.

В операционных системах Windows для ожидания завершения нити служит уже частично рассматривавшаяся универсальная функция ожидания WaitForSingleObject. Для ожидания завершения нити в качестве первого аргумента этой функции следует взять хэндл ожидаемой нити, а в качестве второго - значение предельного времени ожидания, в простейшем случае INFINITE. Каких-либо особенностей, отличающих ее от других форм ожидания, эта функция не имеет при использовании для ожидания нити.

Следующие фрагменты программы, представленные в листинге 8.5.2, демонстрируют простейшее ожидание завершения нити. Отсутствующие части программы совпадают с ее прототипом, записанным в листинге 8.2.2. Программа, восстановленная по указанным фрагментам, функционирует в Windows совершенно так, как было описано выше для программы в листинге 8.5.1, предназначенной для Unix.

#include <windows.h>

Соседние файлы в предмете Операционные системы