SlideShare a Scribd company logo
ˠ˱́˱˼˼˶˼̍˾̌˶˳̌̈˹̂˼˹̃˶˼̍˾̌˶̃˶̆˾˿˼˿˴˹˹ 
˟̂˶˾̍3DUDOOHORPSXWLQJ7HFKQRORJLHV37
˜˶˻̇˹̐326,;7KUHDGV 
˗˹˸˾˶˾˾̌˺̇˹˻˼̀˿̃˿˻˿˳ 
ˠ˼˱˾˹́˿˳˱˾˹˶ˢ˹˾̆́˿˾˹˸˱̇˹̐ 
ˠ˱˸˾˹˻˿˳ˑ˼˶˻̂˶˺ˑ˼˶˻̂˱˾˵́˿˳˹̈ 
˛˱̅˶˵́˱˳̌̈˹̂˼˹̃˶˼̍˾̌̆̂˹̂̃˶˽ˢ˹˲˔ˤˣ˙ 
ˢ˱˺̃˻̄́̂˱KWWSFSFWVLEVXWLVUXaDSD]QLNRYWHDFKLQJ 
˓˿̀́˿̂̌KWWSVSLD]]DFRPVLEVXWLVUXIDOOSFWKRPH
326,;WKUHDGVSWKUHDGV
▪ POSIX: Portable Operating Systems Interface 
for uniX 
▪ Стандартный API для работы с потоками в 
UNIX-подобных системах с 1995 г. 
(IEEE/ANSI 1003.1c-1995) 
▪ Низкоуровневый интерфейс для 
многопоточного программирования в среде 
C/UNIX 
▪ Основа для других высокоуровных моделей 
(C++11-threads, OpenMP, Java threads, etc)
˝˿˵˶˼̍SWKUHDGV 
▪ Модель fork-join: потоки могут порождать (spawn, 
start) другие потоки и ждать их завершения. 
▪ Выполнение потоков планируется ОС, они могут 
выполняться последовательно или 
параллельно, могут назначаться на 
произвольные ядра. 
▪ Потоки работают независимо, асинхронно. 
▪ Для координации доступа должны использоваться 
механизмы (мьютексы, условные переменные и 
др.) взаимного исключения
˗˹˸˾˶˾˾̌˺̇˹˻˼ 
̀˿̃˿˻˿˳
˝˿˵˶˼̍SWKUHDGV 
Поток а 
(основной) 
Поток b 
Поток c 
Поток d 
spawn 
Поток e 
spawn 
spawn 
spawn 
join 
join 
join 
join
˛˱˻˹̂̀˿˼̍˸˿˳˱̃̍ 
▪ Подключить заголовочный файл pthread.h 
▪ Все функции начинаются с префикса pthread_ 
▪ Проверять код ошибки (if (rc != 0) { … }) 
▪ Компилировать 
gcc -Wall -pedantic -O3 -pthread  
-o prog prog.c
ˠ˿́˿˷˵˶˾˹˶̀˿̃˿˻˱ 
int pthread_create(pthread_t *tid, 
const pthread_attr_t *attr, 
void *(*start_routine) (void *) fun, 
void *arg); 
tid - идентификатор потока (для завершения, 
синхронизации и т.д.) 
attr - параметры потока (например, минимальный 
размер стека) 
fun - функция для запуска (принимает и возвращает 
указатель void*) 
arg - аргументы функции
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˱ 
pthread_create() pthread_join(t2) 
pthread_exit(status) 
работа ожидание 
T1 
T2
˟̃˽˶˾˱̀˿̃˿˻˱ 
pthread_cancel(t1) 
работ 
а ожидание 
T1 
T2 
[pthread_exit]
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́ 
void *func_a(void *); 
void *func_b(void *); 
void *func_c(void *); 
void *func_d(void *); 
void *func_e(void *); 
pthread_t ta, tb, tc, td, te, tmain;
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́̀́˿˵
void *func_a(void *arg) 
{ 
sleep(1); 
pthread_create(td, attr, func_d, NULL); 
sleep(3); 
/* same as pthread_exit((void*)123); */ 
return((void *) 123); 
}
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́̀́˿˵
void *func_b(void *arg) 
{ 
sleep(4); 
pthread_exit(NULL); 
}
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́̀́˿˵
void *func_c(void *arg) 
{ 
int err; 
if (err = pthread_join(tmain, status)) { 
printf(“pthread_join failed:%s”, strerror(err)); 
exit(1); 
} 
sleep(1); 
pthread_create(tb, attr, func_b, NULL); 
sleep(4); 
pthread_exit((void *) 321); 
}
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́̀́˿˵
void *func_d(void *arg) 
{ 
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); 
sleep(1); 
pthread_create(te, attr, func_e, NULL); 
sleep(5); 
return((void *) 111); 
}
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́̀́˿˵
void *func_e(void *arg) 
{ 
sleep(3); 
if (err = pthread_join(ta, status)) { 
printf(“pthread_join failed: %s”, 
strerror(err)); 
exit(1); 
} 
sleep(2); 
if (err = pthread_join(tc, status)) { 
printf(“pthread_join failed: %s”, 
strerror(err)); 
exit(1); 
}
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́̀́˿˵
if (err = pthread_join(td, status)) { 
printf(“pthread_join failed: %s”, 
strerror(err)); 
exit(1); 
} 
sleep(1); 
pthread_exit((void *) 88); 
}
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́̀́˿˵
int main() 
{ 
main_thr = pthread_self(); 
sleep(1); 
pthread_create(ta, attr, func_a, NULL); 
sleep(1); 
pthread_create(tc, attr, func_c, NULL); 
sleep(2); 
pthread_cancel(td); 
sleep(1); 
pthread_exit((void *) NULL); 
}
ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́ 
join(A) join(C) E 
работа ожидание 
“зомби” 
join(D) 
D 
A 
main 
C 
B 
create cancel join 
cancel(D) 
join(main)
ˠ˼˱˾˹́˿˳˱˾˹˶˳̌̀˿˼˾˶˾˹̐ 
̀˿̃˿˻˿˳
ˠ˼˱˾˹́˿˳˱˾˹˶˾˱˿̂˾˿˳˶˻˳˱˾̃˿˳˱˾˹̐URXQGURELQVOLFHG
T1 
T2 
работа ожидание
ˣ˿̈˾˿˶̀˼˱˾˹́˿˳˱˾˹˶̂̀́˹˿́˹̃˶̃˱˽˹ 
T1 
T2 
работа ожидание 
запрос ответ
˨˶˽˽˿˷˶̃˲̌̃̍˳̌˸˳˱˾˱˱˻̃˹˳˱̇˹̐̀˿̃˿˻˱ 
1. Синхронизация (synchronization). T1 запрашивает 
мьютекс, и если он занят потоком T2, то Т1 встаёт в 
очередь, тем самым давая возможность другим потокам 
запуститься. 
2. Вытеснение (preemption). Происходит событие, в 
результате которого высокоприоритетный поток Т2 может 
запуститься. Тогда поток Т1 с низким приоритетом 
вытесняется, и Т2 запускается. 
3. Уступание (yielding). Программист явно вызывает 
sched_yield() во время работы Т1, и тогда планировщик 
ищет другой поток Т2, который может запуститься, и 
запускает его. 
4. Квантование (time-slicing). Квант времени для потока Т1 
истёк. Тогда поток Т2 получает квант и запускается.
ˢ˿̂̃˿̐˾˹̐̀˿̃˿˻˱ 
1. Активный (active). Выполняется на 
процессоре. 
2. Готов запуститься (runnable). 
Может и хочет запуститься, но пока 
нет ресурсов. 
3. Сон (sleeping). Ождиает изменения 
состояния переменной синхронизации. 
4. Зомби (zombie). Поток закончил своё 
выполнение и ждёт, когда его ресурсы 
заберут. 
Sync. 
Variabe
ˢ̆˶˽˱̀˶́˶̆˿˵˱̂˿̂̃˿̐˾˹˺̀˿̃˿˻˱ 
Runnable 
Wakeup 
Sleeping Zombie 
Active 
Dispatch 
Preempt 
Exit 
Sleep
ˠ́˹˽˶́̀˼˱˾˹́˿˳˱˾˹̐̀˿̃˿˻˿˳ 
T1 T2 T3 
lock 
unlock 
lock 
Held 1 
Sleepers ⚫ 
Time 
0 
lock 
unlock 
1 lock 
T2 ⚫ 
Held 1 
Sleepers ⚫ 
lock 
unlock 
2 lock Held 0 
Sleepers ⚫ 
lock 
unlock 
3 lock Held 1 
Sleepers ⚫ 
SIG
ˠ́˹˽˶́̀˼˱˾˹́˿˳˱˾˹̐̀˿̃˿˻˿˳ 
T1 T2 T3 
lock 
unlock 
lock 
Held 1 
Sleepers ⚫ 
Time 
0 
lock 
unlock 
1 lock 
T2 ⚫ 
Held 1 
Sleepers ⚫ 
lock 
unlock 
2 lock Held 0 
Sleepers ⚫ 
lock 
unlock 
3 lock Held 1 
Sleepers ⚫ 
SIG 
pthread_mutex_lock(); 
/* критическая секция */ 
pthread_mutex_unlock() 
lock 
unlock
ˠ́˹˽˶́̀˼˱˾˹́˿˳˱˾˹̐̀˿̃˿˻˿˳ 
T1 T2 T3 
lock 
unlock 
lock 
Held 1 
Sleepers ⚫ 
Time 
0 
lock 
unlock 
1 lock 
T2 ⚫ 
Held 1 
Sleepers ⚫ 
lock 
unlock 
2 lock Held 0 
Sleepers ⚫ 
lock 
unlock 
3 lock Held 1 
Sleepers ⚫ 
SIG
˛˱˻˸˱˵˱̃̍̀˿˼˹̃˹˻̄̀˼˱˾˹́˿˳˱˾˹̐˳SWKUHDG 
int pthread_setschedparam(pthread_t thread, 
struct sched_param { 
int sched_priority; 
}; 
int policy, 
const struct sched_param *param); 
int pthread_getschedparam(pthread_t thread, 
int *policy, 
struct sched_param *param); 
Normal policies: 
● SCHED_OTHER 
● SCHED_BATCH 
● SCHED_IDLE 
Real-time policies: 
● SCHED_FIFO 
● SCHED_RR 
● SCHED_DEADLINE 
0 
1..99
˞˿́˽˱˼̍˾˿˶̀˼˱˾˹́˿˳˱˾˹˶6+('B27+(5 
SCHED_OTHER 
● Планирование с разделением времени по 
умолчанию в Linux. 
● Используется динамический приоритет, 
основанный на значении nice value (nice, 
setpriority, sched_setattr), которое повышается 
каждый раз, когда поток может запуститься 
(runnable), но откладывается планировщиком. 
● Обеспечивает справедливое (fair) планирование.
˞˿́˽˱˼̍˾˿˶̀˼˱˾˹́˿˳˱˾˹˶6+('B%$7+ 
SCHED_BATCH (начиная с 2.6.16) 
● Похоже на SCHED_OTHER (планирует потоки в 
соответствии с динамическим приоритетом) 
● Планировщик всегда предполагает, что поток 
требователен к ресурсу процессора (CPU-intensive). 
● Планировщик назначает штрафы (penalty) на 
активацию потока. 
● Для неинтерактивных задач, выполняющих большой 
объем вычислений без уменьшение значения nice или 
● В задачах, для которых требуется детерминированное 
поведение при планировании (пакетная обработка 
задач - batch processing).
˞˿́˽˱˼̍˾˿˶̀˼˱˾˹́˿˳˱˾˹˶6+('B%$7+ 
SCHED_IDLE (начиная с 2.6.23) 
● Планирование низкоприоритетных задач. 
● Приоритет со значением nice ниже +19.
5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B),)2 
Sleeping Runnable 
⬤ 
⬤ 
⬤ 
Приоритет 
T1 ⬤ 
T3 ⬤ T4 ⬤ 
T2
5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B55 
Sleeping Runnable 
⬤ 
⬤ 
⬤ 
Приоритет 
T1 ⬤ 
T3 ⬤ T4 ⬤ 
T2
5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B'($'/,1( 
SCHED_DEADLINE (начиная с 3.14) 
● Спорадическая модель планирования. 
● Основан на алгоритмах GEDF (Global Earliest Deadline 
First) и CBS (Constant Bandwidth Server) 
● Спорадическое пакет задач (sporadic task) - 
последовательность задач, где каждая задача 
запускается не более 1 раза за период. 
● Каждая задача также имеет относительный дедлайн 
(relative deadline), до которого она должна 
завершиться, и время вычислений, в течение 
которого она будет занимать процессор.
5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B'($'/,1( 
SCHED_DEADLINE (начиная с 3.14) 
● Момент, когда пакет задач должен начаться 
выполняться из-за того, что пришла новая задача, 
называется время поступления. 
● Время начала выполнения - это время, когда пакет 
начинает выполняться. 
● Абсолютный дедлайн = относительный дедлайн + 
время поступления.
5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B'($'/,1( 
время выполнения T1 
время поступления 
время начала 
абсолютный дедлайн 
относительный дедлайн 
период
5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B'($'/,1( 
время начала 
абсолютный дедлайн 
время выполнения T1 
время поступления 
sched_runtime 
дедлайн (sched_deadline) 
период (sched_period) 
▪ CBS гарантирует невмешательствуо пакетов между собой путём 
остановки потоков, которые пытаются выполняться больше 
отведённого им времени (runtime) 
▪ Ядро предотвращает ситуации, когда нельзя выполнить 
планирование потоков с политикой SCHED_DEADLINE 
(например, проверяется, достаточно ли будет имеющихся 
процессоров) 
▪ Потоки с политикой SCHED_DEADLINE имеют 
наивысший приоритет! (среди всех других политик)
ˠ́˱˳˹˼˱˹̂̀˿˼̍˸˿˳˱˾˹̐UHDOWLPH̀˿˼˹̃˹˻̀˼˱˾˹́˿˳˱˾˹̐ 
▪ По возможности, никогда не использовать real-time 
политики. 
▪ Если вам требуется полное внимание 
пользователя на чём-то постоянно 
изменяющемся (движение курсора, поток 
видео или аудио) 
▪ Осуществление обратной связи и контроль 
(управление машинами, роботами) 
▪ Сбор и обработка статистики в реальном 
времени.
ˢ˿˳˶̃̌ 
▪ Оптимизируйте работу кэша 
▫ Переключения контекста вызывают 
копирование кэша - это очень долго. 
▫ Используйте привязку выполнения потоков 
к ядрам процессора (processor affinity). 
▪ Если вы много думаете об планировании - 
вероятно, вы делаете что-то не так.
ˢ˹˾̆́˿˾˹˸˱̇˹̐
ˠ́˿˲˼˶˽˱̂˹˾̆́˿˾˹˸˱̇˹˹˹˱̃˿˽˱́˾̌˶˿̀˶́˱̇˹˹ 
Поток A: i = i + 1 
Записать значение 
переменной i в регистр 
(регистр = 5) 
t 
Увеличить содержимое 
регистра (регистр = 6) 
Сохранить значение 
регистра в перменную i 
(регистр = 6) 
Поток B: i = i + 1 
Записать значение 
переменной i в регистр 
(регистр = 5) 
Увеличить содержимое 
регистра (регистр = 6) 
Сохранить значение 
регистра в перменную i 
(регистр = 6) 
i 
5 
5 
6 
6
ˠ́˿˲˼˶˽˱̂˹˾̆́˿˾˹˸˱̇˹˹˹˱̃˿˽˱́˾̌˶˿̀˶́˱̇˹˹ 
Поток 1 Поток 2 
bal = GetBalance(account); 
bal += bal * InterestRate; 
PutBalance(account, bal); 
bal = GetBalance(account); 
bal += deposit; 
PutBalance(account, bal); 
● Критическая секция - участок кода, доступ к которого 
должен обеспечиваться полностью без прерываний. 
● ВСЕ общие данные должны быть защищены. 
● Общие данные - те, к которым могут иметь доступ 
несколько потоков (глобальные, статические 
переменные и др.).
ˠ́˿˲˼˶˽˱̂˹˾̆́˿˾˹˸˱̇˹˹˹˱̃˿˽˱́˾̌˶˿̀˶́˱̇˹˹ 
Поток 1 Поток 2 
bal = GetBalance(account); 
bal += bal * InterestRate; 
PutBalance(account, bal); 
bal = GetBalance(account); 
bal += deposit; 
PutBalance(account, bal); 
Реализация синхронизация требует аппаратной поддержки 
атомарной операции test and set (проверить и установить). 
test_and_set(address) 
{ 
result = Memory[address] 
Memory[address] = 1 
return result; 
} 
Установить новое значение 
(обычно 1) в ячейку и 
возвратить старое 
значение.
ˠ́˿̂̃˶˺̉˱̐́˶˱˼˹˸˱̇˹̐˳˸˱˹˽˾˿˴˿˹̂˻˼̏̈˶˾˹̐ 
// инициализация (к моменту вызова CriticalSection 
// не гарантируется, что skip == false) 
skip = false 
function CriticalSection() { 
while test_and_set(lock) = true 
skip // ждать, пока не получится захватить 
// только один поток может быть в критической секции 
critical section 
// освободить критическую секцию 
lock = false 
}
ˠ́˿̂̃˶˺̉˱̐́˶˱˼˹˸˱̇˹̐˳˸˱˹˽˾˿˴˿˹̂˻˼̏̈˶˾˹̐ 
enter_region: ; Начало функции 
tsl reg, flag ; Выполнить test_and_set. 
; flag - разделяемая переменная, 
; которая копируется в регистр 
; и затем устанавливается в 1. 
cmp reg, #0 ; Был flag равен 0 в entry_region? 
jnz enter_region ; Перейти на начало, если reg ≠ 0 
; то есть flag ≠ 0 на входе 
ret ; Выход. Флаг был равен 0 на входе. 
; Если достигли этой точки, значит 
; мы внутри критической секции! 
leave_region: 
move flag, #0 ; записать 0 во флаг 
ret ; вернуться в вызывающую функцию
˝̍̏̃˶˻̂̌ 
/* инициализация мьютекса */ 
int pthread_mutex_init(pthread_mutex_t *restrict mutex, 
const pthread_mutexattr_t *restrict attr); 
/* или */ 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 
/* уничтожение мьютекса (освобождение ресурсов) */ 
int pthread_mutex_destroy(pthread_mutex_t *mutex); 
/* заблокировать мьютекс */ 
int pthread_mutex_lock(pthread_mutex_t *mutex); 
/* заблокировать мьютекс и не ожидать разблокировки */ 
int pthread_mutex_trylock(pthread_mutex_t *mutex); 
/* разблокировать мьютекс */ 
int pthread_mutex_unlock(pthread_mutex_t *mutex);
˝̍̏̃˶˻̂̌ 
int pthread_mutex_lock(pthread_mutex_t *mutex); 
int pthread_mutex_trylock(pthread_mutex_t *mutex); 
int pthread_mutex_unlock(pthread_mutex_t *mutex); 
T1 
Priority:0 
T2 
Priority:1 
T3 
Priority:2 
Held 1 
Sleepers ⚫ T3 ⚫ 
T2 ⚫ 
lock 
lock lock
˕˹˱˴́˱˽˽˱˳̌̀˿˼˾˶˾˹̐̀˿̃˿˻˿˳̀́˹˽˶́ 
T1 
T2 
работа ожидание 
критическая секция отпереть мьютекс 
запереть мьютекс 
(попытаться)
˕˹˱˴́˱˽˽˱˳̌̀˿˼˾˶˾˹̐̀˿̃˿˻˿˳̀́˹˽˶́ 
T1 
T2 
работа ожидание 
критическая секция отпереть мьютекс 
запереть мьютекс 
(попытаться)
˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ 
void add(list_t *item) 
{ 
pthread_mutex_lock(lock); 
item-next = request; 
list = item; 
pthread_mutex_unlock 
(lock); 
} 
item_t *remove() 
{ 
pthread_mutex_lock(lock); 
item = item-next; 
list = list-next; 
pthread_mutex_unlock(lock); 
return request; 
} 
List Item 3 Item 2 Item 1
˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ 
Поток 1 Поток 2 t 
void add(list_t *item) 
{ 
pthread_mutex_lock(lock); 
item-next = request; 
list = item; 
pthread_mutex_unlock(lock); 
} 
list_t *remove() 
{ 
pthread_mutex_lock(lock); 
/* sleeping */ 
/* sleeping */ 
item = item-next; 
list = list-next; 
pthread_mutex_unlock(lock); 
return request; 
} 
List Item 3 Item 2 Item 1
˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ 
Поток 1 Поток 2 t 
void add(list_t *item) 
{ 
pthread_mutex_lock(lock); 
item-next = request; 
list = item; 
pthread_mutex_unlock(lock); 
} 
list_t *remove() 
{ 
pthread_mutex_lock(lock); 
/* sleeping */ 
/* sleeping */ 
item = item-next; 
list = list-next; 
pthread_mutex_unlock(lock); 
return request; 
} 
List Item 3 Item 2 Item 1 
Item 4 add(item4)
˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ 
Поток 1 Поток 2 t 
void add(list_t *item) 
{ 
pthread_mutex_lock(lock); 
item-next = request; 
list = item; 
pthread_mutex_unlock(lock); 
} 
list_t *remove() 
{ 
pthread_mutex_lock(lock); 
/* sleeping */ 
/* sleeping */ 
item = item-next; 
list = list-next; 
pthread_mutex_unlock(lock); 
return request; 
} 
List Item 4 
Item 3 Item 2 Item 1
˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ 
Поток 1 Поток 2 t 
void add(list_t *item) 
{ 
pthread_mutex_lock(lock); 
item-next = request; 
list = item; 
pthread_mutex_unlock(lock); 
} 
list_t *remove() 
{ 
pthread_mutex_lock(lock); 
/* sleeping */ 
/* sleeping */ 
item = item-next; 
list = list-next; 
pthread_mutex_unlock(lock); 
return request; 
} 
List Item 4 
Item 3 Item 2 Item 1
˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ 
Поток 1 Поток 2 t 
void add(list_t *item) 
{ 
pthread_mutex_lock(lock); 
item-next = request; 
list = item; 
pthread_mutex_unlock(lock); 
} 
list_t *remove() 
{ 
pthread_mutex_lock(lock); 
/* sleeping */ 
/* sleeping */ 
item = item-next; 
list = list-next; 
pthread_mutex_unlock(lock); 
return request; 
} 
List Item 4 
Item 3 Item 2 Item 1 
Поток 3: 
remove()
˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ 
Поток 1 Поток 2 t 
void add(list_t *item) 
{ 
pthread_mutex_lock(lock); 
item-next = request; 
list = item; 
pthread_mutex_unlock(lock); 
} 
list_t *remove() 
{ 
pthread_mutex_lock(lock); 
/* sleeping */ 
/* sleeping */ 
item = item-next; 
list = list-next; 
pthread_mutex_unlock(lock); 
return request; 
} 
Поток 3: 
remove() 
List Item 3 Item 2 Item 1
ˣ̄̀˹˻˿˳̌˶̂˹̃̄˱̇˹˹GHDGORFNV
● Поток пытается дважды захватить один и тот же мьютекс 
● Два мьютекса. Один поток удерживает первый мьютекс и 
пытается запереть второй мьютекс, в то время как второй 
поток удерживает второй мьютекс и пытается запереть 
первый мьютекс. 
T1 T2 
А B 
B А
ˠ́˶˵˿̃˳́˱̊˶˾˹˶˵˶˵˼˿˻˿˳ 
● Жёстко определить порядок захвата ресурсов. 
А B 
А 
А 
B 
T1 
T2 
А B 
B А
ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳ 
int hash_func(fp) { } 
pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER; 
struct elem *ht[SIZE]; 
struct elem { 
int count; 
pthread_mutex_t lock; 
struct elem *next; 
int id; 
/* … */ 
}
ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳ 
/* добавить новый объект */ 
struct elem *elem_alloc(void) { 
struct elem *ep; 
int idx; 
if ((ep = malloc(sizeof(struct elem))) != NULL) { 
ep-count = 1; 
if (pthread_mutex_init(fp-lock, NULL) != 0) { 
free(ep); 
return NULL; 
} 
idx = hash_func(ep); 
pthread_mutex_lock(hashlock); 
ep-next = ht[idx]; 
ht[idx] = ep-next; 
pthread_mutex_lock(ep-lock); 
pthread_mutex_unlock(hashlock); 
/* продолжение инициализации */ 
pthread_mutex_unlock(fp-lock); 
} 
return ep; 
}
ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳ 
/* добавить ссылку на объект */ 
void add_reference(struct elem *ep) { 
pthread_mutex_lock(ep-lock); 
ep-count++; 
pthread_mutex_unlock(ep-lock); 
} 
/* найти существующий объект */ 
struct elem *find_elem(int id) { 
struct elem *ep; 
int idx; 
idx = hash_func(ep); 
pthread_mutex_lock(hashlock); 
for (ep = ht[idx]; ep != NULL; ep = ep-next) { 
if (ep-id == id) { 
add_reference(ep); break; 
} 
} 
pthread_mutex_unlock(hashlock); 
return ep; 
}
ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳ 
void elem_release(struct elem *ep) { /* осободить ссылку на объект */ 
int idx; 
pthread_mutex_lock(ep-lock); 
if (ep-count == 1) { /* последняя ссылка */ 
pthread_mutex_unlock(ep-lock); 
pthread_mutex_lock(hashlock); 
pthread_mutex_lock(ep-lock); 
/* необходима повторная проверка условия */ 
if (ep-count != 1) { 
ep-count--; 
pthread_mutex_unlock(ep-lock); 
pthread_mutex_unlock(hashlock); 
return; 
} 
/* … найти и удалить из списка … */ 
pthread_mutex_unlock(ep-lock); 
pthread_mutex_unlock(hashlock); 
pthread_mutex_destroy(ep-lock); 
} else { 
ep-count--; pthread_mutex_unlock(ep-lock); 
} 
}
ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳̄̀́˿̊˶˾˹˶ 
void elem_release(struct elem *ep) { /* осободить ссылку на объект */ 
int idx; 
pthread_mutex_lock(hashlock); 
if (--ep-count == 0) { /* последняя ссылка */ 
pthread_mutex_unlock(ep-lock); 
pthread_mutex_lock(hashlock); 
pthread_mutex_lock(ep-lock); 
/* необходима повторная проверка условия */ 
if (ep-count != 1) { 
ep-count--; 
pthread_mutex_unlock(ep-lock); 
pthread_mutex_unlock(hashlock); 
return; 
} 
/* … найти и удалить из списка … */ 
pthread_mutex_unlock(ep-lock); 
pthread_mutex_unlock(hashlock); 
pthread_mutex_destroy(ep-lock); 
} else { 
ep-count--; pthread_mutex_unlock(ep-lock); 
} 
}
˓̌˲˿́˻́˹̃˹̈˶̂˻˹̆̂˶˻̇˹˺ 
При разработке многопоточных программ необходимо 
учитывать балланс между эффективностью 
блокировки и её сложностью, которые определяются 
детализацией (“зернистостью”) критической секции. 
● Грубая детализация (coarse-grained) критических 
секций - низкая эффективность, но простота 
разработки и поддержания кода. 
● Мелкая детализация (fine-grained) критических 
секция - высокая эффективность (которая может 
снизиться из-за избыточного количества критических 
секций), но сложность кода
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
/* инициализация мьютекса */ 
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, 
const pthread_rwlockattr_t *restrict attr); 
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 
/* особождение ресурсов, уничтожение мьютекса */ 
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
/* инициализация мьютекса */ 
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, 
const pthread_rwlockattr_t *restrict attr); 
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; 
/* особождение ресурсов, уничтожение мьютекса */ 
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); 
/* захватить мьютекс на чтение */ 
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); 
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); 
/* захватить мьютекс на запись */ 
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); 
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); 
/* освободить мьютекс */ 
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
Current 
writer 
0 
Current 
readers 
1 
Sleeping 
writers ⚫ 
Sleeping 
readers ⚫ 
t 
read 
T1
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
T1 
Current 
writer 
0 
Current 
readers 
2 
Sleeping 
writers ⚫ 
Sleeping 
readers ⚫ 
t 
T2 
read read
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
T1 
Current 
writer 
0 
Current 
readers 
2 
Sleeping 
writers ⚫ 
Sleeping 
readers ⚫ 
t 
T2 T3 
read read 
write 
T3 ⚫
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
T1 
T4 ⚫ 
Current 
writer 
0 
Current 
readers 
2 
Sleeping 
writers ⚫ 
Sleeping 
readers ⚫ 
t 
T2 T3 
read read 
write 
read write 
T4 T5 
T3 ⚫ T5 ⚫
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
T1 
T4 ⚫ 
Current 
writer 
0 
Current 
readers 
2 
Sleeping 
writers ⚫ 
Sleeping 
readers ⚫ 
t 
T2 T3 
read read 
write 
read write 
T4 T5 
T3 ⚫ T5 ⚫ 
read read 
T6 T7 
T6 ⚫ 
T7 ⚫
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
read read write 
T1 
T4 ⚫ 
Current 
writer 
1 
Current 
readers 
0 
Sleeping 
writers ⚫ 
Sleeping 
readers ⚫ 
t 
T2 T3 
read write 
T4 T5 
T5 ⚫ 
read read 
T6 T7 
T6 ⚫ 
T7 ⚫
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
read read write 
T1 
T4 ⚫ 
Current 
writer 
1 
Current 
readers 
0 
Sleeping 
writers ⚫ 
Sleeping 
readers ⚫ 
t 
T2 T3 
T4 T5 
read 
write 
read read 
T6 T7 
T6 ⚫ 
T7 ⚫
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ 
read read write 
T1 
Current 
writer 
0 
Current 
readers 
3 
Sleeping 
writers ⚫ 
Sleeping 
readers ⚫ 
t 
T2 T3 
read write 
T4 T5 
read read 
T6 T7
˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹̀́˹˽˶́ 
void add(item_t *item) 
{ 
pthread_rwlock_wrlock(lock); 
item-next = request; 
list = item; 
pthread_rwlock_unlock(lock); 
} 
item_t *remove() 
{ 
pthread_rwlock_wrlock(lock); 
item = item-next; 
list = list-next; 
pthread_rwlock_unlock(lock); 
return request; 
} 
item_t *find(int id) { 
pthread_rwlock_rdlock(lock); 
for (item = list; item != NULL; item = item-next) { 
if (item-id == id) { 
return item; break; 
} 
} 
pthread_rwlock_unlock(lock); 
}
ˡ˶˻̄́̂˹˳˾̌˶˽̍̏̃˶˻̂̌ 
▪ Рекурсивный мьютекс позволяет одному и тому же 
потоку многократно запирать мьютекс, не отпирая его. 
▪ Мьютекс освобождается тогда, когда количество 
отпираний совпадает с количеством запираний. 
int pthread_mutexattr_getpshared(const pthread_mutexattr_t 
*restrict attr, 
int *restrict pshared); 
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, 
int pshared); 
pshared: 
▫ PTHREAD_MUTEX_NORMAL - стандартный мьютекс, не производит 
дополнительную проверку на наличие ошибок 
▫ PTHREAD_MUTEX_ERRORCHECK - мьютекс, которые выполняет проверку на 
наличие ошибок 
▫ PTHREAD_MUTEX_RECURSIVE - рекурсивный мьютекс
ˡ˶˻̄́̂˹˳˾̌˶˽̍̏̃˶˻̂̌ 
func1 
lock(x-lock) 
... 
func2(x) 
... 
unlock(x-lock) 
func2 
lock(x-lock) 
... 
unlock(x-lock) 
func1(x); 
... 
func2(x);
ˡ˶˻̄́̂˹˳˾̌˶˽̍̏̃˶˻̂̌ˑ˼̍̃˶́˾˱̃˹˳˾̌˺˳˱́˹˱˾̃ 
func1 
lock(x-lock) 
... 
func2_unlocked(x) 
... 
unlock(x-lock) 
func2 
lock(x-lock) 
func2_unlocked() 
unlock(x-lock) 
func1(x); 
... 
func2(x); 
func2_unlocked
ˠ́˿˲˼˶˽˱˙˾˳˶́̂˹̐̀́˹˿́˹̃˶̃˿˳ 
Поток Т1 с низким приоритетом удерживает мьютекс, который 
необходим Т2 с высоким приоритетом. Во время удержания поток Т1 
вытесняется потоком Т3 со средним приоритетом. В результате 
поток Т2 зависит от освобождения мьютекса потоком Т1. 
T1 
Prior: 0 
Runnable 
T2 
Prior: 2 
Sleep 
T3 
Prior: 1 
Active 
lock(M1) 
lock(M1) 
unlock(M1) 
T2 ⚫ 
Held 1 
Sleepers ⚫
ˠ́˿˲˼˶˽˱˙˾˳˶́̂˹̐̀́˹˿́˹̃˶̃˿˳ˡ˶̉˶˾˹̐ 
■ Priority Ceiling Mutex 
Устанавливается максимальный приоритет для потока, который захватывает 
мьютекс. Каждый поток, который захватывает мьютек, автоматически 
получает этот приоритет (даже если у него был ниже). 
int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t 
*restrict attr, int *restrict prioceiling); 
int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, 
int prioceiling); 
int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict mutex, 
int *restrict prioceiling); 
int pthread_mutex_setprioceiling(pthread_mutex_t *restrict mutex, 
int prioceiling, int *restrict old_ceiling); 
■ Priority Inheritance Mutexes 
Поток Т1 захватывает мьютекс без изменения своего приоритета. Когда 
второй поток Т2 пытается захватить, владелец Т1 мьютекса получает 
приоритет потока Т2.
ˠ́˿˲˼˶˽˱ˠ˿̂˼˶˵˿˳˱̃˶˼̍˾̌˺˸˱̆˳˱̃ 
▪ Потоки Т1 и Т2 используют мьютекс М1 для работы, 
удерживая его значительное время. При этом основую 
часть работы они выполняют в критической секции. 
▪ Допустим Т1 захватывает М1. Т2, попытавшись захватить 
мьютекс, засыпает. Тогда, после того, как Т1 освободит 
мьютекс, он может успеть снова его захватить до того, как 
проснётся Т1. 
T1 
работа 
ожидание 
запереть 
(попытаться) отпереть 
критическая секция 
пробуждение 
T2 
Обычный случай
ˠ́˿˲˼˶˽˱ˠ˿̂˼˶˵˿˳˱̃˶˼̍˾̌˺˸˱̆˳˱̃ 
▪ Потоки Т1 и Т2 используют мьютекс М1 для работы, 
удерживая его значительное время. При этом основую 
часть работы они выполняют в критической секции. 
▪ Допустим Т1 захватывает М1. Т2, попытавшись захватить 
мьютекс, засыпает. Тогда, после того, как Т1 освободит 
мьютекс, он может успеть снова его захватить до того, как 
проснётся Т1. 
T1 
работа 
ожидание 
запереть 
(попытаться) отпереть 
критическая секция 
пробуждение 
T2 
Т1 повторно захватывает мьютекс
ˠ́˿˲˼˶˽˱ˠ˿̂˼˶˵˿˳˱̃˶˼̍˾̌˺˸˱̆˳˱̃ˡ˶̉˶˾˹˶ 
Решение - использование FIFO-мьютекса: владелец 
мьютекса (Т1) после освобождения мьютекса 
автоматически передаёт права на захват мьютекса 
первому потоку в очереди, который ожидает освобождения 
мьютекса. 
T1 
работа 
ожидание 
запереть 
(попытаться) отпереть 
критическая секция 
пробуждение 
T2 
Использование FIFO-мьютекса
ˢ˶˽˱̅˿́̌6HPDSKRUHV
▪ Ограничивает число потоков, которые могут 
зайти в заданный участок кода. 
▪ Семафор - это счетчик s = 0, …, ∞ 
▪ Операции: 
▫ sem_post - увеличивает значение семафора 
▫ sem_wait - пытается уменьшить значение 
семафора (и это удаётся сделать, 
если s  0). 
▫ sem_getvalue - вернуть текущее значение 
семафора (используется редко)
ˢ˶˽˱̅˿́̌6HPDSKRUHV
˳SWKUHDGV 
/* проинициализировать семафор */ 
int sem_init(sem_t *sem, int pshared, 
unsigned int value); 
/* уничтожить семафор */ 
int sem_destroy(sem_t *sem); 
/* уменьшить семафор на 1 
* (заблокировать единицу ресурса) */ 
int sem_wait(sem_t *sem); 
int sem_trywait(sem_t *sem); 
int sem_timedwait(sem_t *sem, 
const struct timespec *abs_timeout); 
/* увеличить семафор на 1 
* (разблокировать единицу ресурса) */ 
int sem_post(sem_t *sem);
ˢ˶˽˱̅˿́̌6HPDSKRUHV
̀́˹˽˶́́˱˲˿̃̌ 
sem_wait 
T1 
sem_wait 
T5 ⚫ 
sem_post 
Held 0 
Sleepers ⚫ 
sem_post 
T2 
T3 
T4 
sem_wait 
T5
ˢ˶˽˱̅˿́̌6HPDSKRUHV
̀́˹˽˶́́˱˲˿̃̌ 
работа 
ожидание 
wait 
(попытаться) post 
критическая секция 
T1 
T2 
T3 
T4 
T5
ˢ˶˽˱̅˿́̌ˠ́˹˽˶́̀́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍ 
/* безопасная версия sem_wait, учитывающая 
* прерывания во время выполнения sem_wait */ 
void _sem_wait(sem_t *sem) 
{ 
while (sem_wait(sem) != 0) { } 
} 
/* получить по сети запрос и выделить под него память */ 
request_t *get_request() 
{ 
request_t *request; 
request_t = (request*) malloc(sizeof(request_t)); 
request-data = read_from_net(); 
return request; 
}
ˢ˶˽˱̅˿́̌ˠ́˹˽˶́̀́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍ 
/* обработать поступивший запрос */ 
void process_request(request_t *request) 
{ 
process(request-data); 
free(request); 
} 
/* производитель: получить запрос, добавить в список, 
* увеличить семафор */ 
void *producer(void *arg) { 
request_t *request; 
for (;; ) { 
request = get_request(); 
add(request); 
sem_post(request_lenght); 
} 
}
ˢ˶˽˱̅˿́̌ˠ́˹˽˶́̀́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍ 
/* потребитель: уменьшить семафор, 
* удалить запрос из списка, обработать запрос */ 
void *consumer(void *arg) { 
request_t *request; 
for (;;) { 
_sem_wait(request_lenght); 
request = remove(); 
process_request(request); 
} 
}
ˠ́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍̂˿˴́˱˾˹̈˶˾˹˶˽˵˼˹˾̌˿̈˶́˶˵˹ 
/* производитель ресурсов проверяет, есть ли свободные 
* слоты для нового запроса и добавляет запрос */ 
void *producer(void *arg) 
{ 
request_t *request; 
for (;;) { 
_sem_wait(request_slots); 
request = get_request(); 
add(request); 
sem_post(request_length); 
} 
}
ˠ́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍̂˿˴́˱˾˹̈˶˾˹˶˽˵˼˹˾̌˿̈˶́˶˵˹ 
/* потребитель проверяет, есть ли запросы, 
* затем увеличивает семафор свободных слотов 
* и обрабатывает запрос */ 
void *consumer(void *arg) 
{ 
request_t *request; 
for (;;) { 
_sem_wait(requests_lenght); 
request = remove(); 
process_request(request); 
sem_post(request_slots); 
} 
}
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶RQGLWLRQYDULDEOHV
▪ Условные переменные позволяют потока ожидать 
наступления некоторого события, избегая состояния 
гонки. 
▪ Сами условные переменные защищаются мьютексами. 
▪ Условные переменные - обобщение семафоров. 
/* инициализация условных переменных */ 
int pthread_cond_init(pthread_cond_t *restrict cond, 
const pthread_condattr_t *restrict attr); 
/* или */ 
pthread_cond_t cond = PTHREAD_COND_INITIALIZER; 
/* освободить ресурсы, занимаемые условной переменной */ 
int pthread_cond_destroy(pthread_cond_t *cond);
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶RQGLWLRQYDULDEOHV
/* ожидать наступления события cond */ 
int pthread_cond_wait(pthread_cond_t *restrict cond, 
pthread_mutex_t *restrict mutex); 
/* ожидать наступления события не дольше timeout */ 
int pthread_cond_timedwait(pthread_cond_t *restrict cond, 
pthread_mutex_t *restrict mutex, 
const struct timespec *restrict abstime); 
/* возобновить работу одного потока, 
ожидающего наступления события cond */ 
int pthread_cond_signal(pthread_cond_t *cond); 
/* возобновить работу всех потоков, 
* ожидающих наступления события cond */ 
int pthread_cond_broadcast(pthread_cond_t *cond);
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶RQGLWLRQYDULDEOHV
lock() 
Поток 1 Поток 2 
while (condition != true) 
{ 
unlock() 
sleeping… 
lock() 
} 
unlock() 
/* continue */ 
lock() 
condition = true 
unlock() 
wakeup!
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶RQGLWLRQYDULDEOHV
wait wait wait 
T1 T2 T3 
T4 
signal 
Held 0 
Sleepers ⚫ T1 ⚫ 
T2 ⚫ T3 ⚫
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶RQGLWLRQYDULDEOHV
wait wait wait 
T1 T2 T3 
T4 
signal 
Held 0 
Sleepers ⚫ T1 ⚫ 
T2 ⚫ T3 ⚫ 
pthread_cond_signal
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶RQGLWLRQYDULDEOHV
wait wait wait 
T1 T2 T3 
T4 
signal 
Held 0 
Sleepers ⚫ T2 ⚫ 
T3 ⚫
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶RQGLWLRQYDULDEOHV
wait wait wait 
T1 T2 T3 
T4 
signal 
Held 0 
Sleepers ⚫ T1 ⚫ 
T2 ⚫ T3 ⚫ 
pthread_cond_broadcast
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶RQGLWLRQYDULDEOHV
wait wait wait 
T1 T2 T3 
T4 
signal 
Held 0 
Sleepers ⚫
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ 
Поток 1 Поток 2 
pthread_mutex_lock(m); 
while (!condition) { 
pthread_cond_wait(c, m); 
} 
do_something(); 
pthread_mutex_unlock(m); 
pthread_mutex_lock(m); 
condition = true; 
pthead_mutex_unlock(m); 
pthread_cond_signal(c);
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ 
struct msg { 
struct msg *m_next; 
/* ... другие поля структуры ... */ 
}; 
struct msg *workq; 
pthread_cond_t qready = PTHREAD_COND_INITIALIZER; 
pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ 
void process_msg(void) 
{ 
struct msg *mp; 
for (;;) { 
pthread_mutex_lock(qlock); 
while (workq == NULL) 
pthread_cond_wait(qready, qlock); 
mp = workq; 
workq = mp-m_next; 
pthread_mutex_unlock(qlock); 
/* теперь обрабатываем сообщение */ 
} 
}
ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ 
void enqueue_msg(struct msg *mp) 
{ 
pthread_mutex_lock(qlock); 
mp-m_next = workq; 
workq = mp; 
pthread_mutex_unlock(qlock); 
pthread_cond_signal(qready); 
}
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐ 
lock() 
Поток 1 
while (condition != true) 
{ 
unlock() 
sleeping… 
lock() 
} 
unlock() 
/* continue */ 
pthread_cond_wait(cond, 
mutex);
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
sleeping… 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
sleeping… 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
T1 
работа 
ожидание 
запереть 
(попытаться) отпереть 
критическая секция 
T2 
T3
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
sleeping… 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
sleeping… 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
Поток 1 Поток 2 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
lock() 
while (condition) 
{ 
unlock() 
awakened 
lock() 
} 
unlock() 
/* continue */ 
Поток 3 
pthread_cond_broadcast()
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
T1 
работа 
ожидание 
запереть 
(попытаться) отпереть 
критическая секция 
T2 
T3
ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ 
T1 
работа 
ожидание 
запереть 
(попытаться) отпереть 
критическая секция 
T2 
T3
ˠ́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍̂˿˴́˱˾˹̈˶˾˹˶˽˵˼˹˾̌˿̈˶́˶˵˹ 
/* производитель (на основе условных переменных) */ 
void *producer(void *arg) 
{ 
request_t *request; 
for (;;) { 
pthread_mutex_lock(request_lock); 
while (length = 10) 
pthread_cond_wait(request_producer, 
requests_lock); 
add(request); 
lenght++; 
pthread_mutex_unlock(request_lock); 
pthread_cond_signal(request_consumer); 
} 
}

More Related Content

What's hot

ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
Alexey Paznikov
 
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMДмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Sergey Platonov
 
Lab5
Lab5Lab5
FreeRTOS
FreeRTOSFreeRTOS
FreeRTOSquakke
 
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программированияПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
Alexey Paznikov
 
Модель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, ЯндексМодель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, Яндекс
Yandex
 
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
Alexey Paznikov
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Mikhail Kurnosov
 
Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Семинар 6. Многопоточное программирование на OpenMP (часть 6)Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Mikhail Kurnosov
 
Multiprocessor Programming Intro (lecture 2)
Multiprocessor Programming Intro (lecture 2)Multiprocessor Programming Intro (lecture 2)
Multiprocessor Programming Intro (lecture 2)Dmitry Tsitelov
 
Процессы и потоки. Планирование и взаимоблокировки
Процессы и потоки. Планирование и взаимоблокировкиПроцессы и потоки. Планирование и взаимоблокировки
Процессы и потоки. Планирование и взаимоблокировки
Evgeniy Mironov
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Sergey Platonov
 
Процессы и потоки
Процессы и потокиПроцессы и потоки
Процессы и потоки
Evgeniy Mironov
 
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Alexey Paznikov
 
Применение фреймворка GStreamer в системе видеонаблюдения
Применение фреймворка GStreamer в системе видеонаблюденияПрименение фреймворка GStreamer в системе видеонаблюдения
Применение фреймворка GStreamer в системе видеонаблюдения
corehard_by
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
Mikhail Kurnosov
 
Многопоточные Алгоритмы (для BitByte 2014)
Многопоточные Алгоритмы (для BitByte 2014)Многопоточные Алгоритмы (для BitByte 2014)
Многопоточные Алгоритмы (для BitByte 2014)Roman Elizarov
 
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Yauheni Akhotnikau
 
Павел Довгалюк, Обратная отладка
Павел Довгалюк, Обратная отладкаПавел Довгалюк, Обратная отладка
Павел Довгалюк, Обратная отладка
Sergey Platonov
 

What's hot (20)

ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
ПВТ - осень 2014 - Лекция 4 - Стандарт POSIX Threads. Реентерабельность. Сигн...
 
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVMДмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
Дмитрий Кашицын, Троллейбус из буханки: алиасинг и векторизация в LLVM
 
Lab5
Lab5Lab5
Lab5
 
FreeRTOS
FreeRTOSFreeRTOS
FreeRTOS
 
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программированияПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
ПВТ - весна 2015 - Лекция 4. Шаблоны многопоточного программирования
 
Модель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, ЯндексМодель памяти C++ - Андрей Янковский, Яндекс
Модель памяти C++ - Андрей Янковский, Яндекс
 
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
ПВТ - весна 2015 - Лекция 6. Разработка параллельных структур данных на основ...
 
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
Лекция 7: Многопоточное программирование: часть 3 (OpenMP)
 
Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Семинар 6. Многопоточное программирование на OpenMP (часть 6)Семинар 6. Многопоточное программирование на OpenMP (часть 6)
Семинар 6. Многопоточное программирование на OpenMP (часть 6)
 
Multiprocessor Programming Intro (lecture 2)
Multiprocessor Programming Intro (lecture 2)Multiprocessor Programming Intro (lecture 2)
Multiprocessor Programming Intro (lecture 2)
 
Процессы и потоки. Планирование и взаимоблокировки
Процессы и потоки. Планирование и взаимоблокировкиПроцессы и потоки. Планирование и взаимоблокировки
Процессы и потоки. Планирование и взаимоблокировки
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Процессы и потоки
Процессы и потокиПроцессы и потоки
Процессы и потоки
 
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
Лекция 2. Коллективные операции в MPI. Параллельные алгоритмы случайного блуж...
 
Применение фреймворка GStreamer в системе видеонаблюдения
Применение фреймворка GStreamer в системе видеонаблюденияПрименение фреймворка GStreamer в системе видеонаблюдения
Применение фреймворка GStreamer в системе видеонаблюдения
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
 
Zagursky
ZagurskyZagursky
Zagursky
 
Многопоточные Алгоритмы (для BitByte 2014)
Многопоточные Алгоритмы (для BitByte 2014)Многопоточные Алгоритмы (для BitByte 2014)
Многопоточные Алгоритмы (для BitByte 2014)
 
Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?Для чего мы делали свой акторный фреймворк и что из этого вышло?
Для чего мы делали свой акторный фреймворк и что из этого вышло?
 
Павел Довгалюк, Обратная отладка
Павел Довгалюк, Обратная отладкаПавел Довгалюк, Обратная отладка
Павел Довгалюк, Обратная отладка
 

Similar to ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads

Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммы
Platonov Sergey
 
Иван Пузыревский — Введение в асинхронное программирование
Иван Пузыревский — Введение в асинхронное программированиеИван Пузыревский — Введение в асинхронное программирование
Иван Пузыревский — Введение в асинхронное программирование
Yandex
 
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
Ontico
 
2012 03 14_parallel_programming_lecture05
2012 03 14_parallel_programming_lecture052012 03 14_parallel_programming_lecture05
2012 03 14_parallel_programming_lecture05Computer Science Club
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GIL
Roman Brovko
 
Parallel STL
Parallel STLParallel STL
Parallel STL
Evgeny Krutko
 
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
Alexey Paznikov
 
Вечный вопрос измерения времени
Вечный вопрос измерения времениВечный вопрос измерения времени
Вечный вопрос измерения времени
Tatyanazaxarova
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
Anton Arhipov
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
etyumentcev
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
Mikhail Kurnosov
 
разработка серверов и серверных приложений лекция №2
разработка серверов и серверных приложений лекция №2разработка серверов и серверных приложений лекция №2
разработка серверов и серверных приложений лекция №2
etyumentcev
 
Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...
Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...
Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...Ontico
 
Григорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыГригорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммы
Yandex
 
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Platonov Sergey
 
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)Mikhail Kurnosov
 
iOS-07_2 Multithreading
iOS-07_2 MultithreadingiOS-07_2 Multithreading
iOS-07_2 Multithreading
Noveo
 
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev FedorProgramming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Fedor Lavrentyev
 
JavaScript. Event Loop and Timers (in russian)
JavaScript. Event Loop and Timers (in russian)JavaScript. Event Loop and Timers (in russian)
JavaScript. Event Loop and Timers (in russian)Mikhail Davydov
 

Similar to ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads (20)

Асинхронность и сопрограммы
Асинхронность и сопрограммыАсинхронность и сопрограммы
Асинхронность и сопрограммы
 
Иван Пузыревский — Введение в асинхронное программирование
Иван Пузыревский — Введение в асинхронное программированиеИван Пузыревский — Введение в асинхронное программирование
Иван Пузыревский — Введение в асинхронное программирование
 
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
Оптимизация программ для современных процессоров и Linux, Александр Крижановс...
 
2012 03 14_parallel_programming_lecture05
2012 03 14_parallel_programming_lecture052012 03 14_parallel_programming_lecture05
2012 03 14_parallel_programming_lecture05
 
Linux Kernel Processes
Linux Kernel ProcessesLinux Kernel Processes
Linux Kernel Processes
 
Лекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GILЛекция 13. Многопоточность и GIL
Лекция 13. Многопоточность и GIL
 
Parallel STL
Parallel STLParallel STL
Parallel STL
 
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
ПВТ - весна 2015 - Лекция 3. Реентерабельность. Сигналы. Локальные данные пот...
 
Вечный вопрос измерения времени
Вечный вопрос измерения времениВечный вопрос измерения времени
Вечный вопрос измерения времени
 
JPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profilerJPoint 2016 - Etudes of DIY Java profiler
JPoint 2016 - Etudes of DIY Java profiler
 
разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3разработка серверов и серверных приложений лекция №3
разработка серверов и серверных приложений лекция №3
 
Лекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMPЛекция 6. Стандарт OpenMP
Лекция 6. Стандарт OpenMP
 
разработка серверов и серверных приложений лекция №2
разработка серверов и серверных приложений лекция №2разработка серверов и серверных приложений лекция №2
разработка серверов и серверных приложений лекция №2
 
Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...
Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...
Разработка высокопроизводительных серверных приложений для Linux/Unix (Алекса...
 
Григорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммыГригорий Демченко — Асинхронное программирование и сопрограммы
Григорий Демченко — Асинхронное программирование и сопрограммы
 
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
Павел Сушин «Асинхронное программирование на С++: callbacks, futures, fibers»
 
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
Лекция 7: Фибоначчиевы кучи (Fibonacci heaps)
 
iOS-07_2 Multithreading
iOS-07_2 MultithreadingiOS-07_2 Multithreading
iOS-07_2 Multithreading
 
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev FedorProgramming Java - Lection 06 - Multithreading - Lavrentyev Fedor
Programming Java - Lection 06 - Multithreading - Lavrentyev Fedor
 
JavaScript. Event Loop and Timers (in russian)
JavaScript. Event Loop and Timers (in russian)JavaScript. Event Loop and Timers (in russian)
JavaScript. Event Loop and Timers (in russian)
 

More from Alexey Paznikov

Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Alexey Paznikov
 
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Alexey Paznikov
 
Лекция 4. Производные типы данных в стандарте MPI
Лекция 4. Производные типы данных в стандарте MPIЛекция 4. Производные типы данных в стандарте MPI
Лекция 4. Производные типы данных в стандарте MPI
Alexey Paznikov
 
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
Alexey Paznikov
 
ПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курсаПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курса
Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
Alexey Paznikov
 
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятьюПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
Alexey Paznikov
 
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисленияПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
Alexey Paznikov
 
ПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курсаПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курса
Alexey Paznikov
 
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11
Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10
Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 9
 ТФРВС - весна 2014 - лекция 9 ТФРВС - весна 2014 - лекция 9
ТФРВС - весна 2014 - лекция 9
Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8
Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7
Alexey Paznikov
 
ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6
Alexey Paznikov
 

More from Alexey Paznikov (20)

Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
Лекция 6. Параллельная сортировка. Алгоритмы комбинаторного поиска. Параллель...
 
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
Лекция 5. Метод конечных разностей (параллельные алгоритмы в стандарте MPI)
 
Лекция 4. Производные типы данных в стандарте MPI
Лекция 4. Производные типы данных в стандарте MPIЛекция 4. Производные типы данных в стандарте MPI
Лекция 4. Производные типы данных в стандарте MPI
 
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
Лекция 3. Виртуальные топологии в MPI. Параллельные алгоритмы в стандарте MPI...
 
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
ПВТ - весна 2015 - Лекция 8. Многопоточное программирование без использования...
 
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
ПВТ - весна 2015 - Лекция 5. Многопоточное программирование в С++. Синхрониза...
 
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
ПВТ - весна 2015 - Лекция 1. Актуальность параллельных вычислений. Анализ пар...
 
ПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курсаПВТ - весна 2015 - Лекция 0. Описание курса
ПВТ - весна 2015 - Лекция 0. Описание курса
 
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. М...
 
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++.   Р...
ПВТ - осень 2014 - Лекция 5 - Многопоточное программирование в языке С++. Р...
 
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятьюПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
ПВТ - осень 2014 - Лекция 2 - Архитектура вычислительных систем с общей памятью
 
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисленияПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
ПВТ - осень 2014 - лекция 1 - Введение в параллельные вычисления
 
ПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курсаПВТ - осень 2014 - лекция 1а - Описание курса
ПВТ - осень 2014 - лекция 1а - Описание курса
 
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
Анализ эффективности выполнения алгоритма параллельной редукции в языке Cray ...
 
ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11ТФРВС - весна 2014 - лекция 11
ТФРВС - весна 2014 - лекция 11
 
ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10ТФРВС - весна 2014 - лекция 10
ТФРВС - весна 2014 - лекция 10
 
ТФРВС - весна 2014 - лекция 9
 ТФРВС - весна 2014 - лекция 9 ТФРВС - весна 2014 - лекция 9
ТФРВС - весна 2014 - лекция 9
 
ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8ТФРВС - весна 2014 - лекция 8
ТФРВС - весна 2014 - лекция 8
 
ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7ТФРВС - весна 2014 - лекция 7
ТФРВС - весна 2014 - лекция 7
 
ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6ТФРВС - весна 2014 - лекция 6
ТФРВС - весна 2014 - лекция 6
 

ПВТ - осень 2014 - Лекция 3 - Стандарт POSIX Threads

  • 2. ˜˶˻̇˹̐326,;7KUHDGV ˗˹˸˾˶˾˾̌˺̇˹˻˼̀˿̃˿˻˿˳ ˠ˼˱˾˹́˿˳˱˾˹˶ˢ˹˾̆́˿˾˹˸˱̇˹̐ ˠ˱˸˾˹˻˿˳ˑ˼˶˻̂˶˺ˑ˼˶˻̂˱˾˵́˿˳˹̈ ˛˱̅˶˵́˱˳̌̈˹̂˼˹̃˶˼̍˾̌̆̂˹̂̃˶˽ˢ˹˲˔ˤˣ˙ ˢ˱˺̃˻̄́̂˱KWWSFSFWVLEVXWLVUXaDSD]QLNRYWHDFKLQJ ˓˿̀́˿̂̌KWWSVSLD]]DFRPVLEVXWLVUXIDOOSFWKRPH
  • 4. ▪ POSIX: Portable Operating Systems Interface for uniX ▪ Стандартный API для работы с потоками в UNIX-подобных системах с 1995 г. (IEEE/ANSI 1003.1c-1995) ▪ Низкоуровневый интерфейс для многопоточного программирования в среде C/UNIX ▪ Основа для других высокоуровных моделей (C++11-threads, OpenMP, Java threads, etc)
  • 5. ˝˿˵˶˼̍SWKUHDGV ▪ Модель fork-join: потоки могут порождать (spawn, start) другие потоки и ждать их завершения. ▪ Выполнение потоков планируется ОС, они могут выполняться последовательно или параллельно, могут назначаться на произвольные ядра. ▪ Потоки работают независимо, асинхронно. ▪ Для координации доступа должны использоваться механизмы (мьютексы, условные переменные и др.) взаимного исключения
  • 7. ˝˿˵˶˼̍SWKUHDGV Поток а (основной) Поток b Поток c Поток d spawn Поток e spawn spawn spawn join join join join
  • 8. ˛˱˻˹̂̀˿˼̍˸˿˳˱̃̍ ▪ Подключить заголовочный файл pthread.h ▪ Все функции начинаются с префикса pthread_ ▪ Проверять код ошибки (if (rc != 0) { … }) ▪ Компилировать gcc -Wall -pedantic -O3 -pthread -o prog prog.c
  • 9. ˠ˿́˿˷˵˶˾˹˶̀˿̃˿˻˱ int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(*start_routine) (void *) fun, void *arg); tid - идентификатор потока (для завершения, синхронизации и т.д.) attr - параметры потока (например, минимальный размер стека) fun - функция для запуска (принимает и возвращает указатель void*) arg - аргументы функции
  • 10. ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˱ pthread_create() pthread_join(t2) pthread_exit(status) работа ожидание T1 T2
  • 11. ˟̃˽˶˾˱̀˿̃˿˻˱ pthread_cancel(t1) работ а ожидание T1 T2 [pthread_exit]
  • 12. ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́ void *func_a(void *); void *func_b(void *); void *func_c(void *); void *func_d(void *); void *func_e(void *); pthread_t ta, tb, tc, td, te, tmain;
  • 14. void *func_a(void *arg) { sleep(1); pthread_create(td, attr, func_d, NULL); sleep(3); /* same as pthread_exit((void*)123); */ return((void *) 123); }
  • 16. void *func_b(void *arg) { sleep(4); pthread_exit(NULL); }
  • 18. void *func_c(void *arg) { int err; if (err = pthread_join(tmain, status)) { printf(“pthread_join failed:%s”, strerror(err)); exit(1); } sleep(1); pthread_create(tb, attr, func_b, NULL); sleep(4); pthread_exit((void *) 321); }
  • 20. void *func_d(void *arg) { pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); sleep(1); pthread_create(te, attr, func_e, NULL); sleep(5); return((void *) 111); }
  • 22. void *func_e(void *arg) { sleep(3); if (err = pthread_join(ta, status)) { printf(“pthread_join failed: %s”, strerror(err)); exit(1); } sleep(2); if (err = pthread_join(tc, status)) { printf(“pthread_join failed: %s”, strerror(err)); exit(1); }
  • 24. if (err = pthread_join(td, status)) { printf(“pthread_join failed: %s”, strerror(err)); exit(1); } sleep(1); pthread_exit((void *) 88); }
  • 26. int main() { main_thr = pthread_self(); sleep(1); pthread_create(ta, attr, func_a, NULL); sleep(1); pthread_create(tc, attr, func_c, NULL); sleep(2); pthread_cancel(td); sleep(1); pthread_exit((void *) NULL); }
  • 27. ˠ˿́˿˷˵˶˾˹˶˹˸˱˳˶́̉˶˾˹˶̀˿̃˿˻˿˳̀́˹˽˶́ join(A) join(C) E работа ожидание “зомби” join(D) D A main C B create cancel join cancel(D) join(main)
  • 30. T1 T2 работа ожидание
  • 31. ˣ˿̈˾˿˶̀˼˱˾˹́˿˳˱˾˹˶̂̀́˹˿́˹̃˶̃˱˽˹ T1 T2 работа ожидание запрос ответ
  • 32. ˨˶˽˽˿˷˶̃˲̌̃̍˳̌˸˳˱˾˱˱˻̃˹˳˱̇˹̐̀˿̃˿˻˱ 1. Синхронизация (synchronization). T1 запрашивает мьютекс, и если он занят потоком T2, то Т1 встаёт в очередь, тем самым давая возможность другим потокам запуститься. 2. Вытеснение (preemption). Происходит событие, в результате которого высокоприоритетный поток Т2 может запуститься. Тогда поток Т1 с низким приоритетом вытесняется, и Т2 запускается. 3. Уступание (yielding). Программист явно вызывает sched_yield() во время работы Т1, и тогда планировщик ищет другой поток Т2, который может запуститься, и запускает его. 4. Квантование (time-slicing). Квант времени для потока Т1 истёк. Тогда поток Т2 получает квант и запускается.
  • 33. ˢ˿̂̃˿̐˾˹̐̀˿̃˿˻˱ 1. Активный (active). Выполняется на процессоре. 2. Готов запуститься (runnable). Может и хочет запуститься, но пока нет ресурсов. 3. Сон (sleeping). Ождиает изменения состояния переменной синхронизации. 4. Зомби (zombie). Поток закончил своё выполнение и ждёт, когда его ресурсы заберут. Sync. Variabe
  • 34. ˢ̆˶˽˱̀˶́˶̆˿˵˱̂˿̂̃˿̐˾˹˺̀˿̃˿˻˱ Runnable Wakeup Sleeping Zombie Active Dispatch Preempt Exit Sleep
  • 35. ˠ́˹˽˶́̀˼˱˾˹́˿˳˱˾˹̐̀˿̃˿˻˿˳ T1 T2 T3 lock unlock lock Held 1 Sleepers ⚫ Time 0 lock unlock 1 lock T2 ⚫ Held 1 Sleepers ⚫ lock unlock 2 lock Held 0 Sleepers ⚫ lock unlock 3 lock Held 1 Sleepers ⚫ SIG
  • 36. ˠ́˹˽˶́̀˼˱˾˹́˿˳˱˾˹̐̀˿̃˿˻˿˳ T1 T2 T3 lock unlock lock Held 1 Sleepers ⚫ Time 0 lock unlock 1 lock T2 ⚫ Held 1 Sleepers ⚫ lock unlock 2 lock Held 0 Sleepers ⚫ lock unlock 3 lock Held 1 Sleepers ⚫ SIG pthread_mutex_lock(); /* критическая секция */ pthread_mutex_unlock() lock unlock
  • 37. ˠ́˹˽˶́̀˼˱˾˹́˿˳˱˾˹̐̀˿̃˿˻˿˳ T1 T2 T3 lock unlock lock Held 1 Sleepers ⚫ Time 0 lock unlock 1 lock T2 ⚫ Held 1 Sleepers ⚫ lock unlock 2 lock Held 0 Sleepers ⚫ lock unlock 3 lock Held 1 Sleepers ⚫ SIG
  • 38. ˛˱˻˸˱˵˱̃̍̀˿˼˹̃˹˻̄̀˼˱˾˹́˿˳˱˾˹̐˳SWKUHDG int pthread_setschedparam(pthread_t thread, struct sched_param { int sched_priority; }; int policy, const struct sched_param *param); int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param); Normal policies: ● SCHED_OTHER ● SCHED_BATCH ● SCHED_IDLE Real-time policies: ● SCHED_FIFO ● SCHED_RR ● SCHED_DEADLINE 0 1..99
  • 39. ˞˿́˽˱˼̍˾˿˶̀˼˱˾˹́˿˳˱˾˹˶6+('B27+(5 SCHED_OTHER ● Планирование с разделением времени по умолчанию в Linux. ● Используется динамический приоритет, основанный на значении nice value (nice, setpriority, sched_setattr), которое повышается каждый раз, когда поток может запуститься (runnable), но откладывается планировщиком. ● Обеспечивает справедливое (fair) планирование.
  • 40. ˞˿́˽˱˼̍˾˿˶̀˼˱˾˹́˿˳˱˾˹˶6+('B%$7+ SCHED_BATCH (начиная с 2.6.16) ● Похоже на SCHED_OTHER (планирует потоки в соответствии с динамическим приоритетом) ● Планировщик всегда предполагает, что поток требователен к ресурсу процессора (CPU-intensive). ● Планировщик назначает штрафы (penalty) на активацию потока. ● Для неинтерактивных задач, выполняющих большой объем вычислений без уменьшение значения nice или ● В задачах, для которых требуется детерминированное поведение при планировании (пакетная обработка задач - batch processing).
  • 41. ˞˿́˽˱˼̍˾˿˶̀˼˱˾˹́˿˳˱˾˹˶6+('B%$7+ SCHED_IDLE (начиная с 2.6.23) ● Планирование низкоприоритетных задач. ● Приоритет со значением nice ниже +19.
  • 42. 5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B),)2 Sleeping Runnable ⬤ ⬤ ⬤ Приоритет T1 ⬤ T3 ⬤ T4 ⬤ T2
  • 43. 5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B55 Sleeping Runnable ⬤ ⬤ ⬤ Приоритет T1 ⬤ T3 ⬤ T4 ⬤ T2
  • 44. 5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B'($'/,1( SCHED_DEADLINE (начиная с 3.14) ● Спорадическая модель планирования. ● Основан на алгоритмах GEDF (Global Earliest Deadline First) и CBS (Constant Bandwidth Server) ● Спорадическое пакет задач (sporadic task) - последовательность задач, где каждая задача запускается не более 1 раза за период. ● Каждая задача также имеет относительный дедлайн (relative deadline), до которого она должна завершиться, и время вычислений, в течение которого она будет занимать процессор.
  • 45. 5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B'($'/,1( SCHED_DEADLINE (начиная с 3.14) ● Момент, когда пакет задач должен начаться выполняться из-за того, что пришла новая задача, называется время поступления. ● Время начала выполнения - это время, когда пакет начинает выполняться. ● Абсолютный дедлайн = относительный дедлайн + время поступления.
  • 46. 5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B'($'/,1( время выполнения T1 время поступления время начала абсолютный дедлайн относительный дедлайн период
  • 47. 5HDOWLPH̀˿˼˹̃˹˻˹̀˼˱˾˹́˿˳˱˾˹̐6+('B'($'/,1( время начала абсолютный дедлайн время выполнения T1 время поступления sched_runtime дедлайн (sched_deadline) период (sched_period) ▪ CBS гарантирует невмешательствуо пакетов между собой путём остановки потоков, которые пытаются выполняться больше отведённого им времени (runtime) ▪ Ядро предотвращает ситуации, когда нельзя выполнить планирование потоков с политикой SCHED_DEADLINE (например, проверяется, достаточно ли будет имеющихся процессоров) ▪ Потоки с политикой SCHED_DEADLINE имеют наивысший приоритет! (среди всех других политик)
  • 48. ˠ́˱˳˹˼˱˹̂̀˿˼̍˸˿˳˱˾˹̐UHDOWLPH̀˿˼˹̃˹˻̀˼˱˾˹́˿˳˱˾˹̐ ▪ По возможности, никогда не использовать real-time политики. ▪ Если вам требуется полное внимание пользователя на чём-то постоянно изменяющемся (движение курсора, поток видео или аудио) ▪ Осуществление обратной связи и контроль (управление машинами, роботами) ▪ Сбор и обработка статистики в реальном времени.
  • 49. ˢ˿˳˶̃̌ ▪ Оптимизируйте работу кэша ▫ Переключения контекста вызывают копирование кэша - это очень долго. ▫ Используйте привязку выполнения потоков к ядрам процессора (processor affinity). ▪ Если вы много думаете об планировании - вероятно, вы делаете что-то не так.
  • 51. ˠ́˿˲˼˶˽˱̂˹˾̆́˿˾˹˸˱̇˹˹˹˱̃˿˽˱́˾̌˶˿̀˶́˱̇˹˹ Поток A: i = i + 1 Записать значение переменной i в регистр (регистр = 5) t Увеличить содержимое регистра (регистр = 6) Сохранить значение регистра в перменную i (регистр = 6) Поток B: i = i + 1 Записать значение переменной i в регистр (регистр = 5) Увеличить содержимое регистра (регистр = 6) Сохранить значение регистра в перменную i (регистр = 6) i 5 5 6 6
  • 52. ˠ́˿˲˼˶˽˱̂˹˾̆́˿˾˹˸˱̇˹˹˹˱̃˿˽˱́˾̌˶˿̀˶́˱̇˹˹ Поток 1 Поток 2 bal = GetBalance(account); bal += bal * InterestRate; PutBalance(account, bal); bal = GetBalance(account); bal += deposit; PutBalance(account, bal); ● Критическая секция - участок кода, доступ к которого должен обеспечиваться полностью без прерываний. ● ВСЕ общие данные должны быть защищены. ● Общие данные - те, к которым могут иметь доступ несколько потоков (глобальные, статические переменные и др.).
  • 53. ˠ́˿˲˼˶˽˱̂˹˾̆́˿˾˹˸˱̇˹˹˹˱̃˿˽˱́˾̌˶˿̀˶́˱̇˹˹ Поток 1 Поток 2 bal = GetBalance(account); bal += bal * InterestRate; PutBalance(account, bal); bal = GetBalance(account); bal += deposit; PutBalance(account, bal); Реализация синхронизация требует аппаратной поддержки атомарной операции test and set (проверить и установить). test_and_set(address) { result = Memory[address] Memory[address] = 1 return result; } Установить новое значение (обычно 1) в ячейку и возвратить старое значение.
  • 54. ˠ́˿̂̃˶˺̉˱̐́˶˱˼˹˸˱̇˹̐˳˸˱˹˽˾˿˴˿˹̂˻˼̏̈˶˾˹̐ // инициализация (к моменту вызова CriticalSection // не гарантируется, что skip == false) skip = false function CriticalSection() { while test_and_set(lock) = true skip // ждать, пока не получится захватить // только один поток может быть в критической секции critical section // освободить критическую секцию lock = false }
  • 55. ˠ́˿̂̃˶˺̉˱̐́˶˱˼˹˸˱̇˹̐˳˸˱˹˽˾˿˴˿˹̂˻˼̏̈˶˾˹̐ enter_region: ; Начало функции tsl reg, flag ; Выполнить test_and_set. ; flag - разделяемая переменная, ; которая копируется в регистр ; и затем устанавливается в 1. cmp reg, #0 ; Был flag равен 0 в entry_region? jnz enter_region ; Перейти на начало, если reg ≠ 0 ; то есть flag ≠ 0 на входе ret ; Выход. Флаг был равен 0 на входе. ; Если достигли этой точки, значит ; мы внутри критической секции! leave_region: move flag, #0 ; записать 0 во флаг ret ; вернуться в вызывающую функцию
  • 56. ˝̍̏̃˶˻̂̌ /* инициализация мьютекса */ int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); /* или */ pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; /* уничтожение мьютекса (освобождение ресурсов) */ int pthread_mutex_destroy(pthread_mutex_t *mutex); /* заблокировать мьютекс */ int pthread_mutex_lock(pthread_mutex_t *mutex); /* заблокировать мьютекс и не ожидать разблокировки */ int pthread_mutex_trylock(pthread_mutex_t *mutex); /* разблокировать мьютекс */ int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 57. ˝̍̏̃˶˻̂̌ int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); T1 Priority:0 T2 Priority:1 T3 Priority:2 Held 1 Sleepers ⚫ T3 ⚫ T2 ⚫ lock lock lock
  • 58. ˕˹˱˴́˱˽˽˱˳̌̀˿˼˾˶˾˹̐̀˿̃˿˻˿˳̀́˹˽˶́ T1 T2 работа ожидание критическая секция отпереть мьютекс запереть мьютекс (попытаться)
  • 59. ˕˹˱˴́˱˽˽˱˳̌̀˿˼˾˶˾˹̐̀˿̃˿˻˿˳̀́˹˽˶́ T1 T2 работа ожидание критическая секция отпереть мьютекс запереть мьютекс (попытаться)
  • 60. ˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ void add(list_t *item) { pthread_mutex_lock(lock); item-next = request; list = item; pthread_mutex_unlock (lock); } item_t *remove() { pthread_mutex_lock(lock); item = item-next; list = list-next; pthread_mutex_unlock(lock); return request; } List Item 3 Item 2 Item 1
  • 61. ˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ Поток 1 Поток 2 t void add(list_t *item) { pthread_mutex_lock(lock); item-next = request; list = item; pthread_mutex_unlock(lock); } list_t *remove() { pthread_mutex_lock(lock); /* sleeping */ /* sleeping */ item = item-next; list = list-next; pthread_mutex_unlock(lock); return request; } List Item 3 Item 2 Item 1
  • 62. ˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ Поток 1 Поток 2 t void add(list_t *item) { pthread_mutex_lock(lock); item-next = request; list = item; pthread_mutex_unlock(lock); } list_t *remove() { pthread_mutex_lock(lock); /* sleeping */ /* sleeping */ item = item-next; list = list-next; pthread_mutex_unlock(lock); return request; } List Item 3 Item 2 Item 1 Item 4 add(item4)
  • 63. ˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ Поток 1 Поток 2 t void add(list_t *item) { pthread_mutex_lock(lock); item-next = request; list = item; pthread_mutex_unlock(lock); } list_t *remove() { pthread_mutex_lock(lock); /* sleeping */ /* sleeping */ item = item-next; list = list-next; pthread_mutex_unlock(lock); return request; } List Item 4 Item 3 Item 2 Item 1
  • 64. ˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ Поток 1 Поток 2 t void add(list_t *item) { pthread_mutex_lock(lock); item-next = request; list = item; pthread_mutex_unlock(lock); } list_t *remove() { pthread_mutex_lock(lock); /* sleeping */ /* sleeping */ item = item-next; list = list-next; pthread_mutex_unlock(lock); return request; } List Item 4 Item 3 Item 2 Item 1
  • 65. ˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ Поток 1 Поток 2 t void add(list_t *item) { pthread_mutex_lock(lock); item-next = request; list = item; pthread_mutex_unlock(lock); } list_t *remove() { pthread_mutex_lock(lock); /* sleeping */ /* sleeping */ item = item-next; list = list-next; pthread_mutex_unlock(lock); return request; } List Item 4 Item 3 Item 2 Item 1 Поток 3: remove()
  • 66. ˓̂̃˱˳˻˱˹̄˵˱˼˶˾˹˶˹˸̂̀˹̂˻˱ Поток 1 Поток 2 t void add(list_t *item) { pthread_mutex_lock(lock); item-next = request; list = item; pthread_mutex_unlock(lock); } list_t *remove() { pthread_mutex_lock(lock); /* sleeping */ /* sleeping */ item = item-next; list = list-next; pthread_mutex_unlock(lock); return request; } Поток 3: remove() List Item 3 Item 2 Item 1
  • 68. ● Поток пытается дважды захватить один и тот же мьютекс ● Два мьютекса. Один поток удерживает первый мьютекс и пытается запереть второй мьютекс, в то время как второй поток удерживает второй мьютекс и пытается запереть первый мьютекс. T1 T2 А B B А
  • 69. ˠ́˶˵˿̃˳́˱̊˶˾˹˶˵˶˵˼˿˻˿˳ ● Жёстко определить порядок захвата ресурсов. А B А А B T1 T2 А B B А
  • 70. ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳ int hash_func(fp) { } pthread_mutex_t hashlock = PTHREAD_MUTEX_INITIALIZER; struct elem *ht[SIZE]; struct elem { int count; pthread_mutex_t lock; struct elem *next; int id; /* … */ }
  • 71. ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳ /* добавить новый объект */ struct elem *elem_alloc(void) { struct elem *ep; int idx; if ((ep = malloc(sizeof(struct elem))) != NULL) { ep-count = 1; if (pthread_mutex_init(fp-lock, NULL) != 0) { free(ep); return NULL; } idx = hash_func(ep); pthread_mutex_lock(hashlock); ep-next = ht[idx]; ht[idx] = ep-next; pthread_mutex_lock(ep-lock); pthread_mutex_unlock(hashlock); /* продолжение инициализации */ pthread_mutex_unlock(fp-lock); } return ep; }
  • 72. ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳ /* добавить ссылку на объект */ void add_reference(struct elem *ep) { pthread_mutex_lock(ep-lock); ep-count++; pthread_mutex_unlock(ep-lock); } /* найти существующий объект */ struct elem *find_elem(int id) { struct elem *ep; int idx; idx = hash_func(ep); pthread_mutex_lock(hashlock); for (ep = ht[idx]; ep != NULL; ep = ep-next) { if (ep-id == id) { add_reference(ep); break; } } pthread_mutex_unlock(hashlock); return ep; }
  • 73. ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳ void elem_release(struct elem *ep) { /* осободить ссылку на объект */ int idx; pthread_mutex_lock(ep-lock); if (ep-count == 1) { /* последняя ссылка */ pthread_mutex_unlock(ep-lock); pthread_mutex_lock(hashlock); pthread_mutex_lock(ep-lock); /* необходима повторная проверка условия */ if (ep-count != 1) { ep-count--; pthread_mutex_unlock(ep-lock); pthread_mutex_unlock(hashlock); return; } /* … найти и удалить из списка … */ pthread_mutex_unlock(ep-lock); pthread_mutex_unlock(hashlock); pthread_mutex_destroy(ep-lock); } else { ep-count--; pthread_mutex_unlock(ep-lock); } }
  • 74. ˠ́˹˽˶́́˱˲˿̃̌˵˳̄̆˽̍̏̃˶˻̂˿˳̄̀́˿̊˶˾˹˶ void elem_release(struct elem *ep) { /* осободить ссылку на объект */ int idx; pthread_mutex_lock(hashlock); if (--ep-count == 0) { /* последняя ссылка */ pthread_mutex_unlock(ep-lock); pthread_mutex_lock(hashlock); pthread_mutex_lock(ep-lock); /* необходима повторная проверка условия */ if (ep-count != 1) { ep-count--; pthread_mutex_unlock(ep-lock); pthread_mutex_unlock(hashlock); return; } /* … найти и удалить из списка … */ pthread_mutex_unlock(ep-lock); pthread_mutex_unlock(hashlock); pthread_mutex_destroy(ep-lock); } else { ep-count--; pthread_mutex_unlock(ep-lock); } }
  • 75. ˓̌˲˿́˻́˹̃˹̈˶̂˻˹̆̂˶˻̇˹˺ При разработке многопоточных программ необходимо учитывать балланс между эффективностью блокировки и её сложностью, которые определяются детализацией (“зернистостью”) критической секции. ● Грубая детализация (coarse-grained) критических секций - низкая эффективность, но простота разработки и поддержания кода. ● Мелкая детализация (fine-grained) критических секция - высокая эффективность (которая может снизиться из-за избыточного количества критических секций), но сложность кода
  • 76. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ /* инициализация мьютекса */ int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; /* особождение ресурсов, уничтожение мьютекса */ int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
  • 77. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ /* инициализация мьютекса */ int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr); pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER; /* особождение ресурсов, уничтожение мьютекса */ int pthread_rwlock_destroy(pthread_rwlock_t *rwlock); /* захватить мьютекс на чтение */ int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); /* захватить мьютекс на запись */ int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); /* освободить мьютекс */ int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
  • 78. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ Current writer 0 Current readers 1 Sleeping writers ⚫ Sleeping readers ⚫ t read T1
  • 79. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ T1 Current writer 0 Current readers 2 Sleeping writers ⚫ Sleeping readers ⚫ t T2 read read
  • 80. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ T1 Current writer 0 Current readers 2 Sleeping writers ⚫ Sleeping readers ⚫ t T2 T3 read read write T3 ⚫
  • 81. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ T1 T4 ⚫ Current writer 0 Current readers 2 Sleeping writers ⚫ Sleeping readers ⚫ t T2 T3 read read write read write T4 T5 T3 ⚫ T5 ⚫
  • 82. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ T1 T4 ⚫ Current writer 0 Current readers 2 Sleeping writers ⚫ Sleeping readers ⚫ t T2 T3 read read write read write T4 T5 T3 ⚫ T5 ⚫ read read T6 T7 T6 ⚫ T7 ⚫
  • 83. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ read read write T1 T4 ⚫ Current writer 1 Current readers 0 Sleeping writers ⚫ Sleeping readers ⚫ t T2 T3 read write T4 T5 T5 ⚫ read read T6 T7 T6 ⚫ T7 ⚫
  • 84. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ read read write T1 T4 ⚫ Current writer 1 Current readers 0 Sleeping writers ⚫ Sleeping readers ⚫ t T2 T3 T4 T5 read write read read T6 T7 T6 ⚫ T7 ⚫
  • 85. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹ read read write T1 Current writer 0 Current readers 3 Sleeping writers ⚫ Sleeping readers ⚫ t T2 T3 read write T4 T5 read read T6 T7
  • 86. ˒˼˿˻˹́˿˳˻˱̈̃˶˾˹̐˸˱̀˹̂˹̀́˹˽˶́ void add(item_t *item) { pthread_rwlock_wrlock(lock); item-next = request; list = item; pthread_rwlock_unlock(lock); } item_t *remove() { pthread_rwlock_wrlock(lock); item = item-next; list = list-next; pthread_rwlock_unlock(lock); return request; } item_t *find(int id) { pthread_rwlock_rdlock(lock); for (item = list; item != NULL; item = item-next) { if (item-id == id) { return item; break; } } pthread_rwlock_unlock(lock); }
  • 87. ˡ˶˻̄́̂˹˳˾̌˶˽̍̏̃˶˻̂̌ ▪ Рекурсивный мьютекс позволяет одному и тому же потоку многократно запирать мьютекс, не отпирая его. ▪ Мьютекс освобождается тогда, когда количество отпираний совпадает с количеством запираний. int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr, int *restrict pshared); int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared); pshared: ▫ PTHREAD_MUTEX_NORMAL - стандартный мьютекс, не производит дополнительную проверку на наличие ошибок ▫ PTHREAD_MUTEX_ERRORCHECK - мьютекс, которые выполняет проверку на наличие ошибок ▫ PTHREAD_MUTEX_RECURSIVE - рекурсивный мьютекс
  • 88. ˡ˶˻̄́̂˹˳˾̌˶˽̍̏̃˶˻̂̌ func1 lock(x-lock) ... func2(x) ... unlock(x-lock) func2 lock(x-lock) ... unlock(x-lock) func1(x); ... func2(x);
  • 89. ˡ˶˻̄́̂˹˳˾̌˶˽̍̏̃˶˻̂̌ˑ˼̍̃˶́˾˱̃˹˳˾̌˺˳˱́˹˱˾̃ func1 lock(x-lock) ... func2_unlocked(x) ... unlock(x-lock) func2 lock(x-lock) func2_unlocked() unlock(x-lock) func1(x); ... func2(x); func2_unlocked
  • 90. ˠ́˿˲˼˶˽˱˙˾˳˶́̂˹̐̀́˹˿́˹̃˶̃˿˳ Поток Т1 с низким приоритетом удерживает мьютекс, который необходим Т2 с высоким приоритетом. Во время удержания поток Т1 вытесняется потоком Т3 со средним приоритетом. В результате поток Т2 зависит от освобождения мьютекса потоком Т1. T1 Prior: 0 Runnable T2 Prior: 2 Sleep T3 Prior: 1 Active lock(M1) lock(M1) unlock(M1) T2 ⚫ Held 1 Sleepers ⚫
  • 91. ˠ́˿˲˼˶˽˱˙˾˳˶́̂˹̐̀́˹˿́˹̃˶̃˿˳ˡ˶̉˶˾˹̐ ■ Priority Ceiling Mutex Устанавливается максимальный приоритет для потока, который захватывает мьютекс. Каждый поток, который захватывает мьютек, автоматически получает этот приоритет (даже если у него был ниже). int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *restrict attr, int *restrict prioceiling); int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling); int pthread_mutex_getprioceiling(const pthread_mutex_t *restrict mutex, int *restrict prioceiling); int pthread_mutex_setprioceiling(pthread_mutex_t *restrict mutex, int prioceiling, int *restrict old_ceiling); ■ Priority Inheritance Mutexes Поток Т1 захватывает мьютекс без изменения своего приоритета. Когда второй поток Т2 пытается захватить, владелец Т1 мьютекса получает приоритет потока Т2.
  • 92. ˠ́˿˲˼˶˽˱ˠ˿̂˼˶˵˿˳˱̃˶˼̍˾̌˺˸˱̆˳˱̃ ▪ Потоки Т1 и Т2 используют мьютекс М1 для работы, удерживая его значительное время. При этом основую часть работы они выполняют в критической секции. ▪ Допустим Т1 захватывает М1. Т2, попытавшись захватить мьютекс, засыпает. Тогда, после того, как Т1 освободит мьютекс, он может успеть снова его захватить до того, как проснётся Т1. T1 работа ожидание запереть (попытаться) отпереть критическая секция пробуждение T2 Обычный случай
  • 93. ˠ́˿˲˼˶˽˱ˠ˿̂˼˶˵˿˳˱̃˶˼̍˾̌˺˸˱̆˳˱̃ ▪ Потоки Т1 и Т2 используют мьютекс М1 для работы, удерживая его значительное время. При этом основую часть работы они выполняют в критической секции. ▪ Допустим Т1 захватывает М1. Т2, попытавшись захватить мьютекс, засыпает. Тогда, после того, как Т1 освободит мьютекс, он может успеть снова его захватить до того, как проснётся Т1. T1 работа ожидание запереть (попытаться) отпереть критическая секция пробуждение T2 Т1 повторно захватывает мьютекс
  • 94. ˠ́˿˲˼˶˽˱ˠ˿̂˼˶˵˿˳˱̃˶˼̍˾̌˺˸˱̆˳˱̃ˡ˶̉˶˾˹˶ Решение - использование FIFO-мьютекса: владелец мьютекса (Т1) после освобождения мьютекса автоматически передаёт права на захват мьютекса первому потоку в очереди, который ожидает освобождения мьютекса. T1 работа ожидание запереть (попытаться) отпереть критическая секция пробуждение T2 Использование FIFO-мьютекса
  • 96. ▪ Ограничивает число потоков, которые могут зайти в заданный участок кода. ▪ Семафор - это счетчик s = 0, …, ∞ ▪ Операции: ▫ sem_post - увеличивает значение семафора ▫ sem_wait - пытается уменьшить значение семафора (и это удаётся сделать, если s 0). ▫ sem_getvalue - вернуть текущее значение семафора (используется редко)
  • 98. ˳SWKUHDGV /* проинициализировать семафор */ int sem_init(sem_t *sem, int pshared, unsigned int value); /* уничтожить семафор */ int sem_destroy(sem_t *sem); /* уменьшить семафор на 1 * (заблокировать единицу ресурса) */ int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); /* увеличить семафор на 1 * (разблокировать единицу ресурса) */ int sem_post(sem_t *sem);
  • 100. ̀́˹˽˶́́˱˲˿̃̌ sem_wait T1 sem_wait T5 ⚫ sem_post Held 0 Sleepers ⚫ sem_post T2 T3 T4 sem_wait T5
  • 102. ̀́˹˽˶́́˱˲˿̃̌ работа ожидание wait (попытаться) post критическая секция T1 T2 T3 T4 T5
  • 103. ˢ˶˽˱̅˿́̌ˠ́˹˽˶́̀́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍ /* безопасная версия sem_wait, учитывающая * прерывания во время выполнения sem_wait */ void _sem_wait(sem_t *sem) { while (sem_wait(sem) != 0) { } } /* получить по сети запрос и выделить под него память */ request_t *get_request() { request_t *request; request_t = (request*) malloc(sizeof(request_t)); request-data = read_from_net(); return request; }
  • 104. ˢ˶˽˱̅˿́̌ˠ́˹˽˶́̀́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍ /* обработать поступивший запрос */ void process_request(request_t *request) { process(request-data); free(request); } /* производитель: получить запрос, добавить в список, * увеличить семафор */ void *producer(void *arg) { request_t *request; for (;; ) { request = get_request(); add(request); sem_post(request_lenght); } }
  • 105. ˢ˶˽˱̅˿́̌ˠ́˹˽˶́̀́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍ /* потребитель: уменьшить семафор, * удалить запрос из списка, обработать запрос */ void *consumer(void *arg) { request_t *request; for (;;) { _sem_wait(request_lenght); request = remove(); process_request(request); } }
  • 106. ˠ́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍̂˿˴́˱˾˹̈˶˾˹˶˽˵˼˹˾̌˿̈˶́˶˵˹ /* производитель ресурсов проверяет, есть ли свободные * слоты для нового запроса и добавляет запрос */ void *producer(void *arg) { request_t *request; for (;;) { _sem_wait(request_slots); request = get_request(); add(request); sem_post(request_length); } }
  • 107. ˠ́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍̂˿˴́˱˾˹̈˶˾˹˶˽˵˼˹˾̌˿̈˶́˶˵˹ /* потребитель проверяет, есть ли запросы, * затем увеличивает семафор свободных слотов * и обрабатывает запрос */ void *consumer(void *arg) { request_t *request; for (;;) { _sem_wait(requests_lenght); request = remove(); process_request(request); sem_post(request_slots); } }
  • 109. ▪ Условные переменные позволяют потока ожидать наступления некоторого события, избегая состояния гонки. ▪ Сами условные переменные защищаются мьютексами. ▪ Условные переменные - обобщение семафоров. /* инициализация условных переменных */ int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); /* или */ pthread_cond_t cond = PTHREAD_COND_INITIALIZER; /* освободить ресурсы, занимаемые условной переменной */ int pthread_cond_destroy(pthread_cond_t *cond);
  • 111. /* ожидать наступления события cond */ int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); /* ожидать наступления события не дольше timeout */ int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); /* возобновить работу одного потока, ожидающего наступления события cond */ int pthread_cond_signal(pthread_cond_t *cond); /* возобновить работу всех потоков, * ожидающих наступления события cond */ int pthread_cond_broadcast(pthread_cond_t *cond);
  • 113. lock() Поток 1 Поток 2 while (condition != true) { unlock() sleeping… lock() } unlock() /* continue */ lock() condition = true unlock() wakeup!
  • 115. wait wait wait T1 T2 T3 T4 signal Held 0 Sleepers ⚫ T1 ⚫ T2 ⚫ T3 ⚫
  • 117. wait wait wait T1 T2 T3 T4 signal Held 0 Sleepers ⚫ T1 ⚫ T2 ⚫ T3 ⚫ pthread_cond_signal
  • 119. wait wait wait T1 T2 T3 T4 signal Held 0 Sleepers ⚫ T2 ⚫ T3 ⚫
  • 121. wait wait wait T1 T2 T3 T4 signal Held 0 Sleepers ⚫ T1 ⚫ T2 ⚫ T3 ⚫ pthread_cond_broadcast
  • 123. wait wait wait T1 T2 T3 T4 signal Held 0 Sleepers ⚫
  • 124. ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ Поток 1 Поток 2 pthread_mutex_lock(m); while (!condition) { pthread_cond_wait(c, m); } do_something(); pthread_mutex_unlock(m); pthread_mutex_lock(m); condition = true; pthead_mutex_unlock(m); pthread_cond_signal(c);
  • 125. ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ struct msg { struct msg *m_next; /* ... другие поля структуры ... */ }; struct msg *workq; pthread_cond_t qready = PTHREAD_COND_INITIALIZER; pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;
  • 126. ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ void process_msg(void) { struct msg *mp; for (;;) { pthread_mutex_lock(qlock); while (workq == NULL) pthread_cond_wait(qready, qlock); mp = workq; workq = mp-m_next; pthread_mutex_unlock(qlock); /* теперь обрабатываем сообщение */ } }
  • 127. ˤ̂˼˿˳˾̌˶̀˶́˶˽˶˾˾̌˶ void enqueue_msg(struct msg *mp) { pthread_mutex_lock(qlock); mp-m_next = workq; workq = mp; pthread_mutex_unlock(qlock); pthread_cond_signal(qready); }
  • 128. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐ lock() Поток 1 while (condition != true) { unlock() sleeping… lock() } unlock() /* continue */ pthread_cond_wait(cond, mutex);
  • 129. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() sleeping… lock() } unlock() /* continue */ lock() while (condition) { unlock() sleeping… lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 130. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 131. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 132. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 133. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 134. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 135. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 136. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ T1 работа ожидание запереть (попытаться) отпереть критическая секция T2 T3
  • 137. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() sleeping… lock() } unlock() /* continue */ lock() while (condition) { unlock() sleeping… lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 138. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 139. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ Поток 1 Поток 2 lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ lock() while (condition) { unlock() awakened lock() } unlock() /* continue */ Поток 3 pthread_cond_broadcast()
  • 140. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ T1 работа ожидание запереть (попытаться) отпереть критическая секция T2 T3
  • 141. ˠ́˿˲˼˶˽˱˵˿̀˿˼˾˹̃˶˼̍˾˿˴˿˿˷˹˵˱˾˹̐̂˼̄̈˱˺ T1 работа ожидание запереть (попытаться) отпереть критическая секция T2 T3
  • 142. ˠ́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍̂˿˴́˱˾˹̈˶˾˹˶˽˵˼˹˾̌˿̈˶́˶˵˹ /* производитель (на основе условных переменных) */ void *producer(void *arg) { request_t *request; for (;;) { pthread_mutex_lock(request_lock); while (length = 10) pthread_cond_wait(request_producer, requests_lock); add(request); lenght++; pthread_mutex_unlock(request_lock); pthread_cond_signal(request_consumer); } }
  • 143. ˠ́˿˹˸˳˿˵˹̃˶˼̍̀˿̃́˶˲˹̃˶˼̍̂˿˴́˱˾˹̈˶˾˹˶˽˵˼˹˾̌˿̈˶́˶˵˹ /* потребитель (на основе условных переменных) */ void *consumer(void *arg) { request_t *request; for (;;) { pthread_mutex_lock(request_lock); while (length == 0) pthread_cond_wait(request_consumer, requests_lock); request = remove(); length--; pthread_mutex_unlock(request_lock); pthread_cond_signal(request_producer); process_request(request); } }
  • 144. ˝˿˾˹̃˿́̌ ● Потокобезопасная инкапсюляция общих данных ● В С - путём объявления всех переменных внутри функции, которая выполняет доступ ● В С++ - инкапсюляция путем создания объекта ● Мониторы “заставляют” пользователя совершать действия над общими переменным через код монитора. ● Мониторы, однако, не позволяют реализовать все типы блокировок (например, пересекующиеся блокировки или использование неблокирующих “trylock” блокировок).
  • 145. ˝˿˾˹̃˿́̌ˡ˶˱˼˹˸˱̇˹̐˾˱ˢ /* реализация счётчика count - суммы всех i */ void count(int i) { static int count = 0; static pthread_mutex_t countlock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(countlock); count += i; i = count; pthread_mutex_unlock(countlock); return i; }
  • 146. ˝˿˾˹̃˿́̌ˡ˶˱˼˹˸˱̇˹̐˾˱ˢ class Monitor { pthread_mutex_t *mutex; public: Monitor(pthread_mutex_t *m); virtual ~Monitor(); }; Monitor::Monitor(pthread_mutex_t *m) { mutex = m; pthread_mutex_lock(mutex); } Monitor::~Monitor() { pthread_mutex_unlock(mutex); }
  • 147. ˝˿˾˹̃˿́̌ˡ˶˱˼˹˸˱̇˹̐˾˱ˢ void foo() { Monitor m(data_lock); int temp; // ... func(temp); // ... // Вызов деструктора снимает блокировку }
  • 148. ˢ̀˹˾˼˿˻˹ ▪ Спинлоки подобны мьютексам, но ▪ Это более простой и быстрый механизм синхронизации (“testset” или что-то ещё) ▪ Поток, пытаясь запереть спинлок, не блокируется (если спинлок занят) - вместо этого он циклически пробует запереть спинлок, пока это у него не получится. ▪ Подходит для мелкозернистого параллелизма (КС небольшая и блокируется на короткий срок). ▪ Критическая секция является местом частого возникновения конфликтов. ▪ Для многоядерных систем. ▪ Проблема с приоритетами потоков!
  • 149. ˢ̀˹˾˼˿˻˹ /* инициализация */ int pthread_spin_init(pthread_spinlock_t *lock, int pshared); /* уничтожение */ int pthread_spin_destroy(pthread_spinlock_t *lock); /* запирание мьютекса */ int pthread_spin_lock(pthread_spinlock_t *lock); int pthread_spin_trylock(pthread_spinlock_t *lock); /* отпирание мьютекса */ int pthread_spin_unlock(pthread_spinlock_t *lock);
  • 150. ˢ̀˹˾˼˿˻˹ /* инициализация */ int pthread_spin_init(pthread_spinlock_t *lock, int pshared); /* уничтожение */ int pthread_spin_destroy(pthread_spinlock_t *lock); /* запирание мьютекса */ int pthread_spin_lock(pthread_spinlock_t *lock); int pthread_spin_trylock(pthread_spinlock_t *lock); /* отпирание мьютекса */ int pthread_spin_unlock(pthread_spinlock_t *lock);
  • 151. ˢ̀˹˾˼˿˻˹ pthread_spinlock_t spinlock; rc = pthread_spin_init(spinlock, PTHREAD_PROCESS_PRIVATE); if (rc != 0) /* … */ /* … */ pthread_spin_lock(spinlock); /* “лёгкая” критическая секция, * которая выполняется очень быстро */ if a[i] k count++; pthread_spin_unlock(spinlock); /* … */ pthread_spin_destory(spinlock);
  • 152. ˒˱́̍˶́̌ ▪ Позволяют выполнить синхронизацию для множества потоков. ▪ Достигшие барьера потоки блокируются до тех пор, пока все потоки множества не достигнут барьера. ▫ По достижении барьера, поток уменьшает счётчик барьера и засыпает. ▫ Когда последний поток достигает барьера, он уменьшает счётчик до 0 и разблокирует все потоки.
  • 153. ˒˱́̍˶́̌ ▪ Позволяют выполнить синхронизацию для множества потоков. ▪ Достигшие барьера потоки блокируются до тех пор, пока все потоки множества не достигнут барьера. ▫ По достижении барьера, поток уменьшает счётчик барьера и засыпает. ▫ Когда последний поток достигает барьера, он уменьшает счётчик до 0 и разблокирует все потоки.
  • 154. ˒˱́̍˶́̌ ▪ Позволяют выполнить синхронизацию для множества потоков. ▪ Достигшие барьера потоки блокируются до тех пор, пока все потоки множества не достигнут барьера. ▫ По достижении барьера, поток уменьшает счётчик барьера и засыпает. ▫ Когда последний поток достигает барьера, он уменьшает счётчик до 0 и разблокирует все потоки.
  • 155. ˒˱́̍˶́̌ ▪ Позволяют выполнить синхронизацию для множества потоков. ▪ Достигшие барьера потоки блокируются до тех пор, пока все потоки множества не достигнут барьера. ▫ По достижении барьера, поток уменьшает счётчик барьера и засыпает. ▫ Когда последний поток достигает барьера, он уменьшает счётчик до 0 и разблокирует все потоки.
  • 156. ˒˱́̍˶́̌ ▪ Позволяют выполнить синхронизацию для множества потоков. ▪ Достигшие барьера потоки блокируются до тех пор, пока все потоки множества не достигнут барьера. ▫ По достижении барьера, поток уменьшает счётчик барьера и засыпает. ▫ Когда последний поток достигает барьера, он уменьшает счётчик до 0 и разблокирует все потоки.
  • 157. ˒˱́̍˶́̌ ▪ Позволяют выполнить синхронизацию для множества потоков. ▪ Достигшие барьера потоки блокируются до тех пор, пока все потоки множества не достигнут барьера. ▫ По достижении барьера, поток уменьшает счётчик барьера и засыпает. ▫ Когда последний поток достигает барьера, он уменьшает счётчик до 0 и разблокирует все потоки.
  • 158. ˒˱́̍˶́̌ /* инициализация и уничтожение */ int pthread_barrier_init(pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr, unsigned count); int pthread_barrier_destroy(pthread_barrier_t *barrier); /* синхронизовать потоки в точке вызова функции */ int pthread_barrier_wait(pthread_barrier_t *barrier); /* инициализация уничтожение атрибутов */ int pthread_barrierattr_init(pthread_barrierattr_t *attr); int pthread_barrierattr_destroy(pthread_barrierattr_t *attr); /* задание атрибутов (PTHREAD_PROCESS_SHARED, PTHREAD_PROCESS_PRIVATE)*/ int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict attr, int *restrict pshared); int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared);
  • 159. ˒˱́̍˶́̌ T1 pthread_barrier_wait(B) [B_cntr = 2] pthread_barrier_wait(B) [B_cntr = 3] pthread_barrier_wait(B) [B_cntr = 1] работа T3 T4 ожидание T1 pthread_barrier_wait(B) [B_cntr = 0] pthread_barrier_wait(B,4)
  • 160. ˒˱́̍˶́̌ˠ́˹˽˶́ 1. Проинициализировать массив а. 2. Параллельно домножить все элемента массива на k. 3. Вывести полученный массив на экран. 4. Параллельно вычислить квадратный корень над всеми элементами массива. 5. Вывести полученный массив на экран. 6. Параллельно вычислить остаток от деления на 2 всех элементов массива. 7. Вывести полученный массив на экран. Сколько необходимо барьеров? (если без иных средств синхронизации)
  • 161. ˒˱́̍˶́̌ #include stdio.h #include stdlib.h #include string.h #include math.h #include pthread.h enum { SIZE = 20, NTHREADS = 4, K = 100 }; pthread_barrier_t mult_barrier, sqrt_barrier, output_barrier1, output_barrier2; int *volatile a;
  • 162. ˒˱́̍˶́̌ int main(int argc, const char *argv[]) { pthread_t tid[NTHREADS]; int threadnum[NTHREADS]; int rc, ithr, i; pthread_barrier_init(mult_barrier, NULL, NTHREADS + 1); pthread_barrier_init(sqrt_barrier, NULL, NTHREADS + 1); pthread_barrier_init(output_barrier1, NULL, NTHREADS + 1); pthread_barrier_init(output_barrier2, NULL, NTHREADS + 1); a = (int *) malloc(SIZE * sizeof(int)); /* Serial initialization */ srand(time(NULL)); for (i = 0; i SIZE; i++) { a[i] = rand() % 100; }
  • 163. ˒˱́̍˶́̌ for (ithr = 0; ithr NTHREADS; ithr++) { threadnum[ithr] = ithr; rc = pthread_create(tid[ithr], NULL, thread, threadnum[ithr]); if (rc != 0) { fprintf(stderr, pthread_create failed()); free(a); return 1; } } pthread_barrier_wait(mult_barrier); printf(nArray after 1st stage:n); for (i = 0; i SIZE; i++) printf(%d , a[i]); fflush(stdout); pthread_barrier_wait(output_barrier1); pthread_barrier_wait(sqrt_barrier);
  • 164. ˒˱́̍˶́̌ printf(nnArray after 2nd stage:n); for (i = 0; i SIZE; i++) printf(%d , a[i]); fflush(stdout); pthread_barrier_wait(output_barrier2); for (ithr = 0; ithr NTHREADS; ithr++) pthread_join(tid[ithr], NULL); printf(nnArray after 3rd stage:n); for (i = 0; i SIZE; i++) printf(%d , a[i]); pthread_barrier_destroy(mult_barrier); pthread_barrier_destroy(sqrt_barrier); pthread_barrier_destroy(output_barrier1); pthread_barrier_destroy(output_barrier2); free(a); return 0; }
  • 165. ˒˱́̍˶́̌ void *thread(void *arg) { int i, *threadnum = (int *) arg; int lower_bound = *threadnum * (SIZE / NTHREADS); int upper_bound = lower_bound + SIZE / NTHREADS; for (i = lower_bound; i upper_bound; i++) a[i] = a[i] * K; pthread_barrier_wait(mult_barrier); pthread_barrier_wait(output_barrier1); for (i = lower_bound; i upper_bound; i++) a[i] = sqrt(a[i]); pthread_barrier_wait(sqrt_barrier); pthread_barrier_wait(output_barrier2); for (i = lower_bound; i upper_bound; i++) { a[i] = a[i] % 2; return NULL; }