Неблокирующие алгоритмы final.pptx
- Количество слайдов: 38
Неблокирующие алгоритмы Доклад на семинаре по специальности Студент группы 4057/2 Антон Рычагов 11. 02. 2018
Содержание Введение 1. Многопоточность 2. Взаимное исключение и его недостатки 3. Атомарные операции 4. Неблокирующие алгоритмы 5. Примеры неблокирующих алгоритмов 6. Проблемы, присущие неблокирующим алгоритмам 7. Реализации в современных языках программирования Заключение 11. 02. 2018 2
Введение Многопото чность — свойство платформы, состоящее в том, что процесс, порождённый в операционной системе, может состоять из нескольких потоков, выполняющихся параллельно Конкуренция – процессы часто соперничают за доступ к критическим ресурсам, владение которыми разрешается только одному процессу в данных момент времени Три проблемы: • Взаимное исключение • Взаимная блокировка • Голодание 11. 02. 2018 3
Потокобезопасность Часть кода называется потокобезопасной, если при использовании ей совместных ресурсов им гарантируется безопасное использование несколькими потоками одновременно. Как защищаться? • Реентерабельность кода • Локальные хранилища потоков • Неизменяемые объекты (immutable) Эти подходы ориентированы на избежание совместного состояния ресурсов 11. 02. 2018 4
Взаимоисключающие алгоритмы – алгоритмы, для выполнения которых нужно изолировать или блокировать доступ к части данных class Account { double balance; void withdraw(double amount) { balance -= amount; } void deposit(double amount) { balance += amount; } void transfer(Account from, Account to, double amount) { lock(from); lock(to); from. withdraw(amount); to. deposit(amount); release(to); release(from); } } 11. 02. 2018
Чем плохи взаимоисключающие алгоритмы? 1. Вызывают блокировку потока и вынуждают его ждать освобождения примитива синхронизации 2. Поддержка блокировки – это дополнительные накладные расходы 3. Выгрузка или зацикливание потока, захватившего примитив синхронизации, может привести к бесконечному ожиданию этого примитива 4. Тупики (deadlocks) – ситуация, когда два или более соревнующихся потока ожидают завершения друга 5. Голодание потока 6. Инверсия приоритетов – высокоприоритетный поток «застревает» на активном ожидании 11. 02. 2018
Атомарные операции - операции, выполняющиеся как единое целое, либо не выполняющиеся вовсе Примеры атомарных операций: • Сложение • Вычитание • Обмен (Exchange) • Проверить и установить (Test & Set) • Выбрать и добваить (Fetch & Add) • Сравнить и обменять (Compare & Swap) Зачастую атомарная операция представляет собой ассемблерную инструкцию, однако это не обязательно. Не каждая ассемблерная операция – атомарна! Примеры не атомарных операций: • Положить на стек (push) • Взять со стека (pop) 11. 02. 2018 7
Compare & Swap С&S – атомарная инстукция, доступная на большинстве процессоров Основное применение – реализация примитивов синхронизации (семафоры, мьютексы) и неблокирующих алгоритмов int compare_and_swap (int* reg, int oldval, int newval) { int old_reg_val = *reg; if (old_reg_val == oldval) *reg = newval; return old_reg_val; } 11. 02. 2018 8
Неблокирующие алгоритмы Конец ХХ века: Неблокирующие алгоритмы – алгоритмы, для которых точно известно, что выполнения потоков, выполняющих данный алгоритм, не вызовет взаимного исключения при конкуренции за совместные ресурсы ХХI век: Неблокирующие алгоритмы – алгоритмы, приостановка которых в одном или более потоках не вызовет остановки прогресса оставшихся потоков Избавление от критических секций! Неблокирующие алгоритмы используют атомарные примитивы чтенияизменения-записи, которые предоставлены аппаратурой компьютера, наиболее используемым из которых является «Сравнить и обменять» (C&S) 11. 02. 2018 9
Виды неблокирующих алгоритмов Без блокировки (lock-free) Гарантия прогресса Без ожидания (wait-free) Без барьеров (obstruction-free) 11. 02. 2018 10
Отсутствие ожидания (wait-free) Отсутствие ожидания алгоритма - все операции связаны с количеством шагов, которое сделает алгоритм, до того как данная операция завершится Проблемы: • Очень тяжело реализовать • Тяжело выиграть в производительности, по сравнению с взаимоисключающим аналогом 2011 год: первая реализация неблокирующей очереди без ожидания основанной на C&S операциях Это самая сильная гарантия прогресса процесса в целом, сочетающая в себе отсутствие голодания и хорошую пропускную способность системы! 11. 02. 2018 11
Отсутствие блокировок (lock-free) Синхронизация является свободной от блокировок (lock-free) если она удовлетворяет следующему условию: когда потоки программы выполняются достаточно долго, то по крайней мере один поток делает прогресс Push(T *obj) { n = new Node(obj); n->Val = obj; do { n->Next = Top; } while (!CAS(&Top, n, n->Next)); } T *Pop() { for (; ; ) { Node *t = Top; if (t == 0) return 0; if (CAS(&Top, t->Next, t)) { T *res = t->Val; delete t; // проблема return res; } } } Позволяет голодать некоторым потокам, но гарантирует прогресс как минимум одного потока 11. 02. 2018 12
Проблема двойного считывания Чаще используется выражение «Проблема АВА» Проблема ABA возникает при синхронизации, когда ячейка памяти читается дважды, оба раза прочитано одинаковое значение, и признак «значение одинаковое» трактуется как «ничего не менялось» Поток может выполниться между этими двумя чтениями, поменять значение, сделать что-нибудь ещё, и восстановить старое значение. Таким образом, исходный поток обманется, считая, что не поменялось ничего. А 11. 02. 2018 В А 13
Проблема двойного считывания (2) Лишь некоторые алгоритмы страдают проблемой АВА! Пример: связный список Ожидаемый результат выполнения операции pop(): 11. 02. 2018 14
Проблема двойного считывания (3) «Внезапный» результат выполнения операции pop(): 11. 02. 2018 15
Проблема двойного считывания (4) Как «лечить» проблему АВА? • • • Добавить дополнительные биты «метки» в проверяемое значение Использование промежуточных узлов, которые не являются пользовательскими данными, а обеспечивают инвариантность операций добавления и удаления Использовать один или несколько hazard pointer’ов (указателей опасности) 11. 02. 2018 16
Отсутствие барьеров (obstruction-free) Отсутствие барьеров в синхронизации: в любой момент времени одиночный поток запускается изолированно от остальных и завершит своё выполнение за ограниченное число шагов Синхронизация с помощью мьютексов не отвечает этому требованию: если поток остановится, захватив мьютекс, то остальные потоки, которым этот мьютекс нужен, будут простаивать! Это самая слабая гарантия прогресса процесса в целом! Obstr. Free 11. 02. 2018 Lock. Free Wait. Free 17
Неблокирующий счетчик Обычная блокирующая синхронизация: public final class Counter { private long value = 0; public synchronized long get. Value() { return value; } БЛОКИРОВКА public synchronized long increment() { return ++value; } } 11. 02. 2018 18
Неблокирующий счетчик (2) Неблокирующая синхронизация: public class Nonblocking. Counter { private Atomic. Integer value; public int get. Value() { return value. get(); } public int increment() { int v; do { v = value. get(); } while (!value. compare. And. Set(v, v + 1)); return v + 1; } } 11. 02. 2018 Atomic. Integer – класс атомарного целого числа, стандартный класс java. util. concurrent начиная с Java 5. 0 Compare & Set гарантирует, что смена значения ‘value’ будет атомарной 19
Неблокирующий счетчик (3) При измерении производительности потоки выполняли 5000 операций inc() и get() 7000 Время, ms 6000 5000 4000 Blocking 3000 Non-blocking 2000 1000 0 1 4 16 64 256 1024 4096 Количество потоков 11. 02. 2018 20
Неблокирующий стек Atomic. Reference<Node<E>> head = new Atomic. Reference<Node<E>>(); public void push(E item) { Node<E> new. Head = new Node<E>(item); Node<E> old. Head; do { old. Head = head. get(); new. Head. next = old. Head; } while (!head. compare. And. Set(old. Head, new. Head)); Atomic. Reference – класс } атомарной ссылки, public E pop() { стандартный класс Node<E> old. Head; java. util. concurrent начиная с Java Node<E> new. Head; 5. 0 do { old. Head = head. get(); if (old. Head == null) return null; new. Head = old. Head. next; } while (!head. compare. And. Set(old. Head, new. Head)); return old. Head. item; } 11. 02. 2018 21
Неблокирующий стек (2) Работа функции push(): 11. 02. 2018 22
Неблокирующий стек (3) Работа функции pop(): 11. 02. 2018 23
Неблокирующий стек (4) При измерении производительности потоки выполняли 5000 операций push() и pop() 10000 9000 Время, ms 8000 7000 6000 5000 Blocking 4000 Non-blocking 3000 2000 1000 0 1 4 16 64 256 1024 4096 Количество потоков 11. 02. 2018 24
Неблокирующая очередь Сложность по сравнению с предыдущими примерами: очередь требует изменения более одного указателя. C&S разрешает атомарные условные обновления одного указателя, но не двух Поэтому, для создания неблокирующих связного списка (а также дерева или хеш-таблицы) нужно найти способ изменения нескольких указателей при помощи C&S, не оставляющий структуру данных в противоречивом состоянии Существует атомарная операция C&S 2, действия которой аналогичны двум C&S операциям. Но из-за присущих ей проблем она не реализуется в S подавляющем большинстве современных процессоров 11. 02. 2018 25
Неблокирующая очередь (2) private Atomic. Reference<Node<E>> head = new Atomic. Reference<Node<E>>(new Node<E>(null, null)); private Atomic. Reference<Node<E>> tail = head; public boolean put(E item) { Node<E> new. Node = new Node<E>(item, null); while (true) { Node<E> cur. Tail = tail. get(); Node<E> residue = cur. Tail. next. get(); if (cur. Tail == tail. get()) { if (residue == null) { if (cur. Tail. next. compare. And. Set(null, new. Node)) { tail. compare. And. Set(cur. Tail, new. Node); return true; } else { tail. compare. And. Set(cur. Tail, residue); } } } 11. 02. 2018 26
Неблокирующая очередь (3) 1. Очередь с двумя элементами в изначальном состоянии 2. Очередь с тремя элементами в промежуточном состоянии 3. Очередь с тремя элементами в конечномсостоянии 11. 02. 2018 27
Неблокирующая очередь(4) При измерении производительности потоки выполняли 5000 операций put() 3500 Время, ms 3000 2500 2000 Blocking 1500 Non-blocking 1000 500 0 1 4 16 64 256 1024 Количество потоков 11. 02. 2018 28
Неблокирующий бит-массив Бит-массив – набор последовательно записанных двоичных разрядов 11. 02. 2018 29
Неблокирующая хэш-таблица get(key) функция: 11. 02. 2018 30
Неблокирующая хэш-таблица (2) put(key, newval) функция: 11. 02. 2018 31
Основные проблемы неблокирующих алгоритмов • Livelocks Это слово означает такую ситуацию: поток не блокируется, а находиться в занятом ожидании, его состояние постоянно меняется — но, тем не менее, он зациклился. Поток 1: get. Locks 12(lock 1, lock 2) { lock 1. lock(); while(lock 2. locked()){ lock 1. unlock(); wait(); lock 1. lock(); } } Поток 2: get. Locks 21(lock 1, lock 2) { lock 2. lock(); while(lock 1. locked()) { lock 2. unlock(); wait(); lock 2. lock(); } } Если потоки станут помогать другу устранить тупик, то они войдут в livelock 11. 02. 2018 32
Основные проблемы неблокирующих алгоритмов (2) • Условие гонки (race condition) Это опасная ошибка проектирования многопоточной системы или приложения, при которой работа системы или приложения зависит от того, в каком порядке выполняются части кода int x; // Поток 1: while (!stop) { x++; … } // Поток 2: while (!stop) { if(x % 2==0) System. out. println("x =" + x); … } 11. 02. 2018 33
Основные проблемы неблокирующих алгоритмов (3) Существует лишь единственный способ избежать условия гонки – это взаимоисключение доступа Почему условие гонки опасно? Therac-25 – аппарат радиолучевой терапии, в котором проявлялась данная проблема. Из-за переоблучения погибло по меньшей мере шесть человек, некоторые пациенты получили дозы в десятки тысяч рад. 11. 02. 2018 34
Реализации неблокирующих алгоритмов в современных ЯП Стандартная библиотека – пакет java. util. concurrent – доступен с Java 5. 0. Реализуются через std: : atomic Queue. py – неблокирующая очередь Thread: : Queue – неблокирующая очередь 11. 02. 2018 35
Заключение Плюсы: • Неблокирующие алгоритмы предоставляют потокобезопасность кода • Отлично работают с огромными массивами данных • В среднем более производительны, нежели аналогичные блокирующие алгоритмы Минусы: • Более сложны в реализации, чем блокирующие алгоритмы • Зачастую бывает чрезвычайно трудно доказать корректность алгоритма 11. 02. 2018 36
СПАСИБО ЗА ВНИМАНИЕ! 11. 02. 2018 37
Литература • http: //ru. wikipedia. org/wiki/ • http: //www. ibm. com/developerworks/ru/libr ary/j-jtp 04186/ • https: //www. ibm. com/developerworks/java/li brary/j-jtp 11234/ • http: //en. wikipedia. org/wiki/Concurrent_data _structure • http: //www. data-race. com/2011/02/04/lockfree-readers-writers/ 11. 02. 2018 38
Неблокирующие алгоритмы final.pptx