Главная страница  Взаимодействие нетривиальных процессов 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 [ 166 ] 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

48 {

49 i nt i;

50 for (i = 0: i < nloop: i++) {

51 Pthread mutexJock(&shared->niutex):

52 shared->counter++:

53 Pthread mutex unlock(&shared->niutex):

54 }

55 return(NULL);

56 }



ПРИЛОЖЕНИЕ Б

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

Б.1. Введение

в этом приложении приведены основные функции, используемые для работы с потоками. В традиционной модели Unix процесс, которому нужно, чтобы какое-то действие было выполнено не им самим, порождает дочерний процесс вызовом fork. Большая часть сетевых серверов под Unix написана именно так.

Хотя эта парадигма хорошо работала на протяжении многих лет, вызов fork обладает некоторыми недостатками:

а вызов fork ресурсоемок. Память копируется от родительского процесса к дочернему, копируются все дескрипторы и т. д. Существующие реализации используют метод копирования при записи (copy-on-write), что исключает необходимость копирования адресного пространства родительского процесса, пока оно не понадобится клиенту, но, несмотря на эту оптимизацию, вызов fork остается ресурсоемким;

Ж для передачи информации между родительским и дочерним процессами необходимо использовать одну из форм IPC после вызова fork. Передать информацию дочернему процессу легко: это можно сделать до вызова fork. Однако передать ее обратно может быть достаточно сложно.

Потоки помогают решить обе проблемы. Часто они называются облегченными процессами (lightweight processes), поскольку поток проще, чем процесс. Создание потока может занимать по времени меньше одной десятой создания процесса.

Все потоки одного процесса совместно используют его глобальные переменные, поэтому им легко обмениваться информацией, но это приводит к необходимости синхронизации. Однако общими становятся не только глобальные переменные. Все потоки одного процесса разделяют:

ж инструкции процесса;

ш большую часть данных;

открытые файлы (дескрипторы);

ш обработчики сигналов и вообще настройки для работы с сигналами;

11 текущий рабочий каталог;

Ж идентификатор пользователя и группы.



Однако каждый поток имеет свой собственный: ш идентификатор потока;

ш набор регистров, включая РС и указатель стека;

Ш стек (для локальных переменных и адресов возврата);

ш еггпо;

ш маску сигналов; ш приоритет.

Б.2. Основные функции для работы

с потоками: создание и завершение

в этом разделе мы опишем пять основных функций для работы с потоками.

Функция pthread create

При запуске программы вызовом exec создается единственный поток, называемый начальным потоком, или главным (initial thread). Добавочные потоки создаются вызовом pthread create: linclude <pthread.h>

int pthread create(pthread t Hid. const pthread attr t *attr. void *(.*func) (void *). void *arg):

/* Возвращает 0 в случае успешного завершения, положительное значение Еххх - в случае ошибки */

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

Каждый поток обладает некоторым количеством атрибутов: приоритетом, начальным размером стека, признаком демона и т. п. При создании потока эти атрибуты могут быть указаны с помощью переменной типа pthread attr t, значение которой имеет более высокий приоритет, чем значения по умолчанию. Обычно мы используем значения по умолчанию. При этом аргумент attr является нулевым указателем.

Наконец, при создании потока мы должны указать функцию, которую он будет выполнять, - начальную функцию потока (thread start function). Поток запускается вызовом этой функции и завершается либо явно (вызовом pthread exi t), либо неявно (возвратом из этой функции). Адрес функции указывается в аргументе func, и вызывается она с единственным аргументом - указателем arg. Если функции нужно передать несколько аргументов, следует упаковать их в структуру и передать ее адрес в качестве единственного аргумента начальной функции.

Обратите внимание на объявления func и arg. Функция принимает один аргумент - указатель типа voi d, и возвращает один аргумент - такой же указатель. Это дает нам возможность передать потоку указатель на что угодно и получить в ответ такой же указатель.

Функции Posix для работы с потоками обычно возвращают О в случае успешного завершения работы и ненулевое значение в случае ошибки. В отличие от



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 [ 166 ] 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.