ПВТ - осень 2014 - Лекция 7. Многопоточное...
-
Upload
alexey-paznikov -
Category
Education
-
view
644 -
download
2
Transcript of ПВТ - осень 2014 - Лекция 7. Многопоточное...
![Page 1: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/1.jpg)
Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель. Потокобезопасный стек
Пазников Алексей АлександровичКафедра вычислительных систем СибГУТИ
Сайт курса: http://cpct.sibsutis.ru/~apaznikov/teaching/Вопросы: https://piazza.com/sibsutis.ru/fall2014/pct14/home
Параллельные вычислительные технологииОсень 2014 (Parallel Computing Technologies, PCT 14)
![Page 2: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/2.jpg)
Программирование без блокировок
2
Если вы думаете, что программирование без блокировок это просто, значит или вы - один из тех 50, которые умеют
это делать, или же используете атомарные инструкции недостаточно аккуратно.
Герб Саттер
![Page 3: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/3.jpg)
Цели многопоточного программирования без блокировок
3
Однопоточная программа
Многопоточная программа с блокировками
Многопоточная программа без блокировок
![Page 4: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/4.jpg)
Цели многопоточного программирования без блокировок
▪ Повышение масштабируемости путём сокращения блокировок и ожиданий в алгоритмах и структурах данных.
▪ Решение проблем, связанных с блокировками:
▫ Гонки: нужно не забыть заблокировать, причём именно нужный участок кода
▫ Дедлоки: необходимо запирать в нужном порядке различные потоки
▫ Сложность выбора критической секции (простота или масштабируемость)
▫ Голоданеие, инверсия приоритетов и др.4
![Page 5: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/5.jpg)
Виды алгоритмов, свободных от блокировок
▪ Свободные от ожиданий (wait-free). “Никто никогда не ждёт”. Каждая операция завершается за N шагов без каких-либо условий. Гарантии:
▫ максимум пропускной способности системы
▫ отсутствие голодания
▪ Свободные от блокировок (lock-free). “Всегда какой-то из потоков работает”. Каждый шаг приближает итоговое решение. Гарантии:
▫ максимум пропускной способности системы
▫ отсутствие голодания (один поток может постоянно ожидать)
▪ Свободные от остановок (obstruction-free). “Поток работает, если нет конфликтов”. За ограниченное число шагов один поток, при условии, что другие потоки остановлены, достигает результата.
▫ Все потоки не блокируются из-за проблем (задержек, ошибок) с другими потоками.
▫ Не гарантируется прогресс, если одновременно работают два или больше потоков. 5
![Page 6: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/6.jpg)
Реализация спинлока на основе атомарного флага
class spinlock_mutex { std::atomic_flag flag;
public: spinlock_mutex(): flag{ATOMIC_FLAG_INIT} { }
void lock() { while (flag.test_and_set( std::memory_order_acquire)); }
void unlock() { flag.clear(std::memory_order_release); }};
n.b. Можно использовать с lock_guard и unique_guard! 6
![Page 7: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/7.jpg)
Реализация модели потребитель-производитель без блокировок
7
![Page 8: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/8.jpg)
Производитель-потребитель на основе блокировок
8
Производитель
Потребитель
Потребитель
Потребитель
![Page 9: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/9.jpg)
Производитель-потребитель на основе блокировок
void producer() {
while (ThereAreTasks()) { task = BuildNewTask(); std::unique_lock<std::mutex> lock{mut}; queue.push(task); lock.unlock(); cv.notify(); }
std::unique_lock<std::mutex> lock{mut}; queue.push(done); // добавить признак окончания lock.unlock(); cv.notify();}
9
![Page 10: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/10.jpg)
Производитель-потребитель на основе блокировок
void consumer() { task = nullptr; while (task != done) { std::unique_lock<std::mutex> lock{mut}; cv.wait(mut, []{ return queue.empty(); }); task = queue.first(); if (task != done) queue.pop(); }
if (task != done) DoWork(task);}
10
![Page 11: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/11.jpg)
Производитель-потребитель без блокировок
11
is empty?
empty
is empty?
is empty?
empty
is empty?
is empty?
empty
is empty?
is empty?
empty
is empty?
![Page 12: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/12.jpg)
Производитель-потребитель без блокировок
12
put out
is empty?
is empty?
empty
put out
full
is empty?
empty full
is empty?
is empty?
is empty?
![Page 13: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/13.jpg)
Производитель-потребитель без блокировок
13
is empty?
is empty?
empty
put in
full
is empty?
empty full
is empty?
process process
is empty?
![Page 14: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/14.jpg)
Производитель-потребитель без блокировок
14
is empty?
is empty?
put in
full
is empty?
empty full
is empty?
process process
full
is empty?
![Page 15: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/15.jpg)
Производитель-потребитель без блокировок
15
is empty?
is empty?
full
is empty?
full
is empty?
is empty?
empty fulldone done
put indone flag
put output out
![Page 16: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/16.jpg)
Производитель-потребитель без блокировок
16
is empty?
full
is empty?
full
is empty?
done donefull
done
put out
empty
is empty?
put indone flag
![Page 17: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/17.jpg)
Производитель-потребитель без блокировок
17
full
is empty?
full
is empty?
done donefull
done
is empty?
is empty?
donefull
put out
![Page 18: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/18.jpg)
Производитель-потребитель без блокировок
18
full
is empty?
full
is empty?
done donefull
done
is empty?
is empty?
donefull
![Page 19: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/19.jpg)
Производитель-потребитель без блокировок
19
Empty Task Done
Start
End
Производитель
Потребитель
![Page 20: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/20.jpg)
Производитель
20
curr = 0; // указатель на текущий слотwhile (ThereAreMoreTasks()) { task = AllocateAndBuildNewTask(); while (slot[curr] != null) // если null, то проверить curr = (curr + 1) % numOfConsumers; // следующий слот slot[curr] = task; sem[curr].signal();}
![Page 21: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/21.jpg)
Производитель
21
curr = 0; // указатель на текущий слотwhile (ThereAreMoreTasks()) { task = AllocateAndBuildNewTask(); while (slot[curr] != null) // если null, то проверить curr = (curr + 1) % numOfConsumers; // следующий слот slot[curr] = task; sem[curr].signal();}
// Фаза 2: выставить флаги “done” во всех слотахnumNotified = 0;while (numNotified < numOfConsumers) { while (slot[curr] != null) // если null, то проверить curr = (curr + 1) % numOfConsumers; // следующий slot[curr] = done; // освободить слот sem[curr].signal(); ++numNotified;}
![Page 22: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/22.jpg)
Потребитель
22
task = null;while (task != done)
// Дождаться, когда слот будет полным while ((task = slot[mySlot]) == null) sem[mySlot].wait();
if (task != done) { slot[mySlot] = null; // помечаем слот пустым DoWork(task); // выполняем задачу } // вне критической секции}
![Page 23: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/23.jpg)
Потребитель
23
task = null;while (task != done)
// Дождаться, когда слот будет полным while ((task = slot[mySlot]) == null) sem[mySlot].wait();
if (task != done) { slot[mySlot] = null; // помечаем слот пустым DoWork(task); // выполняем задачу } // вне критической секции}
Как применить модель памяти С++?
![Page 24: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/24.jpg)
Производитель, модель памяти С++
24
curr = 0; while (ThereAreMoreTasks()) { task = AllocateAndBuildNewTask();
while (slot[curr] != null) // acquire null curr = (curr + 1) % numOfConsumers; slot[curr] = task; // release non-null sem[curr].signal();}// Фаза 2: выставить флаги “done” во всех слотахnumNotified = 0;while (numNotified < numOfConsumers) { while (slot[curr] != null) // acquire null curr = (curr + 1) % numOfConsumers; slot[curr] = done; // release done sem[curr].signal(); ++numNotified;}
![Page 25: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/25.jpg)
Птребитель, модель памяти С++
25
task = null;while (task != done)
// Дождаться, когда слот будет полным while ((task = slot[mySlot]) // acquire non-null == null) sem[mySlot].wait();
if (task != done) { slot[mySlot] = null; // release null DoWork(task); } }
![Page 26: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/26.jpg)
Производитель-потребитель, класс алгоритма
26
curr = 0; while (ThereAreMoreTasks()) { task = AllocateAndBuildNewTask();
while (slot[curr] != null) // acquire null curr = (curr + 1) % numOfConsumers; slot[curr] = task; // release non-null sem[curr].signal();}// Фаза 2: выставить флаги “done” во всех слотахnumNotified = 0;while (numNotified < numOfConsumers) { while (slot[curr] != null) // acquire null curr = (curr + 1) % numOfConsumers; slot[curr] = done; // release done sem[curr].signal(); ++numNotified;}
Алгоритм - свободный от ожиданий, свободный от блокировок или свободный от остановок?
![Page 27: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/27.jpg)
Производитель-потребитель, класс алгоритма
27
curr = 0; while (ThereAreMoreTasks()) { task = AllocateAndBuildNewTask();
while (slot[curr] != null) // acquire null curr = (curr + 1) % numOfConsumers; slot[curr] = task; // release non-null sem[curr].signal();}// Фаза 2: выставить флаги “done” во всех слотахnumNotified = 0;while (numNotified < numOfConsumers) { while (slot[curr] != null) // acquire null curr = (curr + 1) % numOfConsumers; slot[curr] = done; // release done sem[curr].signal(); ++numNotified;}
Алгоритм - свободный от ожиданий, свободный от блокировок или свободный от остановок?
Этап 2:Свободная от остановок
Этап 1:Свободный от ожиданий
![Page 28: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/28.jpg)
Производитель-потребитель без блокировок
28
task = null;while (task != done)
// Дождаться, когда слот будет полным while ((task = slot[mySlot]) == null) sem[mySlot].wait();
if (task != done) { slot[mySlot] = null; DoWork(task); } }
можно ли поменять две строки?нужно ли это сделать?
![Page 29: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/29.jpg)
Реализация стека, свободного от блокировок, на основе сборщика мусора
29
![Page 30: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/30.jpg)
Стек, свободный от блокировок
30
T T T T
head
1. Конструктор
2. Деструктор
3. Поиск узла (find)
4. Добавление узла (push)
5. Удаление узла (pop)
![Page 31: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/31.jpg)
Стек, свободный от блокировок
31
template <typename T>class lfstack {public: lfstack(); ~lfstack(); T* find(T data) const; // найти элемент, равный data void push(T data); // добавить элемент в голову
private: struct node { // атомарные операции T data; // не требуются node* next; }; std::atomic<node*> head{nullptr}; // атомарный указатель}; // на голову списка
![Page 32: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/32.jpg)
Конструктор и деструктор
32
template <typename T>lfstack<T>::lfstack() {}
Объект создаётся в одном потоке, поэтому не нужно обеспечивать параллельный доступ. Нельзя использовать стек до тех пор, пока он не будет создан, т.е. до выполнения конструктора, и после того, как он будет уничтожен, т.е. после выполнения деструктора.
template <typename T>lfstack<T>::~lfstack() { auto first = head.load(); while (first) { auto unlinked = first; first = first->next; delete unlinked; }}
![Page 33: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/33.jpg)
Функция push
33
1. Создать новый узел.
2. Записать в его указатель next текущее значение head.
3. Записать в head указатель на новый узел.
void push(T const& data) { auto new_node = new node{data}; // (1) node_node->next = head.load(); // (2) head = new_node; // (3)}
struct node { T data; node* next; node(T const& _data): data{_data} {}};
![Page 34: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/34.jpg)
Функция push
34
T
head
TTT
Начальная стадия
Промежуточная стадия
T
head
TTT
T
Конечная стадия
T
head
TTT
T
![Page 35: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/35.jpg)
Функция push
35
T
head
TTT
Начальная стадия
Промежуточная стадия выполняется двумя потоками
T
head
TTT
T
Первый добавляемый элемент пропал, остался только второй
T
head
TTT
T
T T
![Page 36: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/36.jpg)
Функция push
36
void push(T const& data) { auto new_node = new node{data}; // (1) node_node->next = head.load(); // (2) head = new_node; // (3) while (!head.compare_exchange_weak(new_node->next, new_node)); // (3)}
1. Создать новый узел.
2. Записать в его указатель next текущее значение head.
3. Записать в head указатель на новый узел, при этом с помощью операции сравнить-и-обменять гарантировать то, что head не был модифицирован с момента чтения на шаге 2.
![Page 37: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/37.jpg)
Функция pop (ошибочная)
37
void pop(T& result) { node* old_head = head.load(); head = head->next; result = old_head->data; }
![Page 38: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/38.jpg)
Функция pop
38
T
head
TTT
Начальная стадия
Промежуточная стадия
T
head
TTT
Конечная стадия
T
head
TT
![Page 39: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/39.jpg)
Функция pop
39
T
head
TTT
Начальная стадия
Промежуточная стадия
T
head
TTT
Конечная стадия
T
head
TT
B
A
A B
![Page 40: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/40.jpg)
Функция pop (ошибочная)
40
void pop(T& result) { node* old_head = head.load();
while (!head.compare_exchange_weak(old_head, old_head->next); result = old_head->data; }
![Page 41: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/41.jpg)
Функция pop (ошибочная)
41
void pop(T& result) { node* old_head = head.load();
while (!head.compare_exchange_weak(old_head, old_head->next); result = old_head->data; }
![Page 42: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/42.jpg)
Функция pop (ошибочная)
42
void pop(T& result) { node* old_head = head.load();
while (!head.compare_exchange_weak(old_head, old_head->next); result = old_head->data; }
std::shared_ptr<T> pop(T& result) { node* old_head = head.load(); while (old_head && !head.compare_exchange_weak(old_head, old_head->next)); return old_head ? old_head->data : std::shared_ptr<T>(); }
![Page 43: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/43.jpg)
Функция pop (ошибочная)
43
class lfstack {private: struct node { std::shared_ptr<T> data; node* next; node(T const& _data): data(std::make_shared<T>(_data)) { } };
...
std::shared_ptr<T> pop(T& result) { node* old_head = head.load(); while (old_head && !head.compare_exchange_weak(old_head, old_head->next)); return old_head ? old_head->data : std::shared_ptr<T>(); }
![Page 44: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/44.jpg)
Проблема АВА
44
4
old_head
321
head->nextПоток А выполняет удаление узла
из вершины стека
![Page 45: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/45.jpg)
Проблема АВА
45
4
old_head
321
head->next
43
Поток А выполняет удаление узла из вершины стека
old_head old_head->nextПоток А был вытеснен и другие
потоки удалили два узла из стека
![Page 46: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/46.jpg)
Проблема АВА
46
4
old_head
321
head->next
43
Поток А был вытеснен и другие потоки удалили два узла из стека
Поток А выполняет удаление узла из вершины стека
435
old_head old_head->next
old_head old_head->nextНекий поток добавил новый узел и
аллокатор выделил под него ту же память
![Page 47: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/47.jpg)
Проблема АВА
47
4
old_head
321
head->next
43
Поток А выполняет удаление узла из вершины стека
43
Некий поток добавил новый узел и аллокатор выделил под него ту же память
5
43
Поток А: head.compare_exchange( old_head, old_head->next))
5
old_head old_head->next
old_head old_head->next
old_head old_head->next
Поток А был вытеснен и другие потоки удалили два узла из стека
![Page 48: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/48.jpg)
Проблема АВА
48
4
old_head
321
head->next
43
Поток А выполняет удаление узла из вершины стека
435
43
Некий поток добавил новый узел и аллокатор выделил под него ту же память
Поток А: head.compare_exchange( old_head, old_head->next))
5
old_head old_head->next
old_head old_head->next
old_head old_head->next
Поток А был вытеснен и другие потоки удалили два узла из стека
![Page 49: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/49.jpg)
Решения проблемы АВА
49
1. “Ленивый” сборщик мусора
2. Указатели опасности
3. Счётчик ссылок на элемент
4. Сделать узлы уникальными
5. Вообще не удалять узлы
6. Добавление дополнительных узлов
7. и т.д.
![Page 50: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/50.jpg)
Функция pop (наивная)
50
// количество потоков, выполняющих popstd::atomic<unsigned> threads_in_pop;
std::shared_ptr<T> pop() { threads_in_pop++;
node* old_head = head_load(); while (old_head && !head.compare_exchange_weak(old_head, old_head->next)); std::shared_ptr<T> res; if (old_head) res.swap(old_head->data); // не копировать, // а обменять данные
try_reclaim(old_head); // попробовать освободить // удалённые узлы return res;}
![Page 51: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/51.jpg)
Функция pop (наивная)
51
template<typename T>class lfstack {private: std::atomic<node*> delete_list;
static void delete_nodes(node* nodes); while (nodes) { node* next = nodes->next; delete nodes; nodes = next; } }
![Page 52: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/52.jpg)
Функция try_reclaim освобождения удалённых узлов
52
void try_reclaim(node* old_head) { if (threads_in_pop == 1) { // я единственный в pop?
node* nodes_to_delete = // захватить список delete_list.exchange{nullptr}; // на удаление
if (!--thread_in_pop) // точно единственный? delete_nodes(nodes_to_delete)); // удалить всё! else if (nodes_to_delete) // если в захваченном списке // что-то было // вернуть это в общий список узлов на удаление chain_pending_nodes(nodes_to_delete);
delete old_head; // удаляем хотя бы только что // исключённый узел } else { // удалим узел как-нибудь потом chain_pending_node(old_head); --threads_in_pop; }}
![Page 53: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/53.jpg)
Функция try_reclaim освобождения удалённых узлов
53
// добавляем захваченный список в общий список узлов,// подлежащих удалениюvoid chain_pending_nodes(node* nodes) { node* last = nodes; while (node* const next = last->next) last = next; chain_pending_nodes(nodes, last);}
// добавить список узлов в список узлов на удалениеvoid chain_pending_nodes(node* first, node* last) { last->next = delete_list; while (!delete_list.compare_exchange_weak(last->next, first));}
// добавить узел в список узлов на удалениеvoid chain_pending_node(node* n) { chain_pending_nodes(n, n);}
![Page 54: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/54.jpg)
Функция try_reclaim освобождения удалённых узлов
54
4
head
321
delete_list 0threads_in_pop
4
head
321
delete_list
threads_in_pop 1
5
5A A удаляет узел 1 и
вытесняется в pop() после 1-го чтения threads_in_pop
![Page 55: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/55.jpg)
Функция try_reclaim освобождения удалённых узлов
55
4
head
32
delete_list 2threads_in_pop
43
delete_list
threads_in_pop 2
5
2
С удаляет узел и продолжает работать до момента выхода из pop()
old_head
BB вызывает pop() и
вытесняется после 1-го чтения head
A
headold_head
B4
C
5
A
![Page 56: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/56.jpg)
Функция try_reclaim освобождения удалённых узлов
56threads_in_pop 2
43
delete_list
threads_in_pop 2
2
A возобновляет выполнение и захватывает список на удаление. После этого он должен 2-й раз проверить,
один ли он в pop()
headold_head
B2
5
A
43
headold_head
B2
delete_list A2 5
delete_list
B возобновляет выполнение, выполняет CAS и переходит
к 3 узлу
![Page 57: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/57.jpg)
Реализация стека, свободного от блокировок, на основе указателей опасности
57
![Page 58: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/58.jpg)
Указатели опасности (hazard pointers)
58
4
old_head
321
head->next
43
Поток А выполняет удаление узла из вершины стека и помечает узел 1
как узел, который он использует.
old_head old_head->next
Поток А был вытеснен и другие потоки удалили два узла из стека, но не
освобождают память из-под первого узла.
2
head
head
1
A “понимает”, что головной узел head изменился и нужно
выполнить compare_exchange
43
old_head old_head->next
21
![Page 59: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/59.jpg)
Функция pop на основе указателей опасности
59
std::shared_ptr<T> pop() { std::atomic<void*>& hp = get_hazard_pointer_for_current_thread();
// установить указатель опасности перед чтением указателя, // который мы собираемся разыменовывать node* old_head = head.load(); node* temp; do { temp = old_head; hp.store(old_head); old_head = head.load(); } while (old_head != temp); // ...}
![Page 60: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/60.jpg)
Функция pop на основе указателей опасности
60
std::shared_ptr<T> pop() { std::atomic<void*>& hp = get_hazard_pointer_for_current_thread();
node* old_head = head.load(); do { node* temp; do { temp = old_head; hp.store(old_head); // устанавливаем УО old_head = head.load(); } while (old_head != temp); } while (old_head && // получаем узел !head.compare_exchange_strong(old_head, old_head->next)); hp.store(nullptr);
![Page 61: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/61.jpg)
Указатели опасности (hazard pointers)
61
4
old_head
321head
hp
![Page 62: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/62.jpg)
Указатели опасности (hazard pointers)
62
4
old_head
321head
temp = old_head
temp
hp
![Page 63: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/63.jpg)
Указатели опасности (hazard pointers)
63
4
old_head
321head
hp
temp = old_head
temp
hp.store(old_head)
![Page 64: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/64.jpg)
Указатели опасности (hazard pointers)
64
4
old_head
321
temp = old_head
head
temp
hp.store(old_head)
old_head = head.load()“old old_head”
“new old_head”
hp
![Page 65: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/65.jpg)
Указатели опасности (hazard pointers)
65
4
old_head
321
temp = old_head
head
temp
hp.store(old_head)
old_head = head.load()“old old_head”
“new old_head”
hp
== ?
Таким образом, внутренний цикл гарарантирует то, что указатель опасности будет указывать на тот головной элемент head, с котором
мы будем работать (сдвигать указатель на следующий элемент)
Проверка позволяет определить, не изменился ли головной элемент с тех пор, когда мы запомнили его в указателе опасности.
![Page 66: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/66.jpg)
Указатели опасности (hazard pointers)
66
4
old_head
321
temp = old_head
head
temp
hp.store(old_head)
old_head = head.load()“old old_head”
“new old_head”
hp
== ?
Во внешнем цикле сдвигаем указатель с head на следующий элемент с уверенностью, что никто не подменит элемент head.
![Page 67: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/67.jpg)
Указатели опасности (hazard pointers)
67
После того, как поток А успешно выполнил compare_exchange,
указатель опасности можно обнулять hp.store(nullptr), т.к. никто пока
не сможет удалить old_head, кроме А, поскольку head изменён потоком А
43
old_head old_head->next
21
Вариант 1
43
old_head->next
1
Вариант 2
old_head
2
![Page 68: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/68.jpg)
Функция pop на основе указателей опасности
68
std::shared_ptr<T> res; if (old_head) { res.swap(old_head->data); // извлекаем данные
if (outstanding_hazard_pointers_for(old_head)) // если опасно, удаляем потом reclaim_later(old_head); else // если не опасно, удаляем сейчас delete old_head;
// пробуем удалить узлы, какие можно удалить delete_nodes_with_no_hazards(); } return res;}
![Page 69: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/69.jpg)
Реализация указателей опасности
69
4321head
1 5 6 7 m432
max_hazard_pointers
![Page 70: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/70.jpg)
Реализация указателей опасности
70
4321head
1 5 6 7 m432
Указатели опасности, m = max_hazard_pointers
пустой?нет
if (hazard_pointers[i].id. compare_exchange_strong( old_id, std::this_thread::get_id()))
thread_local hp
![Page 71: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/71.jpg)
Реализация указателей опасности
71
4321head
1 5 6 7 m432
Указатели опасности, m = max_hazard_pointers
пустой?да
if (hazard_pointers[i].id. compare_exchange_strong( old_id, std::this_thread::get_id()))
thread_local hp
![Page 72: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/72.jpg)
Реализация указателей опасности
72
4321head
1 5 6 7 m432
Указатели опасности, m = max_hazard_pointers
пустой?да
if (hazard_pointers[i].id. compare_exchange_strong( old_id, std::this_thread::get_id()))
thread_local hp
![Page 73: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/73.jpg)
Реализация указателей опасности
73
const auto max_hazard_pointers = 100;
struct hazard_pointer { std::atomic<std::thread::id> id; std::atomic<void*> pointer;};
hazard_pointer hazard_pointers[max_hazard_pointers];
class hp_owner { hazard_pointer* hp;
public: hp_owner(hp_owner const&) = delete; hp_owner operator=(hp_owner const&) = delete;
![Page 74: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/74.jpg)
Реализация указателей опасности
74
hp_owner(): hp{nullptr} { for (auto i = 0; i < max_hazard_pointers; i++) { std::thread::id old_id; // пустой незанятый УО
// если i-й УО не занят, завладеть им, записав в него // свой идентификатор потока if (hazard_pointers[i].id.compare_exchange_strong( old_id, std::this_thread::get_id())) { hp = &hazard_pointers[i]; // я владею i-м УО break; } }
// таблица УО закончилась, указателей нам не досталось if (!hp) throw std::runtime_error("No hazard ptrs available");}
![Page 75: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/75.jpg)
Реализация указателей опасности
75
hp_owner(): hp{nullptr} { for (auto i = 0; i < max_hazard_pointers; i++) { std::thread::id old_id; if (hazard_pointers[i].id.compare_exchange_strong( old_id, std::this_thread::get_id())) { hp = &hazard_pointers[i]; break; } } if (!hp) throw std::runtime_error("No hazard ptrs available");}std::atomic<void*>& get_pointer() { return hp->pointer;}~hp_owner() { hp->pointer.store(nullptr); hp->id.store(std::thread::id());}
![Page 76: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/76.jpg)
Реализация указателей опасности
76
// вернуть указатель опасности для текущего потокаstd::atomic<void*>& get_hazard_pointer_for_current_thread() { thread_local static hp_owner hazard; return hazard.get_pointer();}
![Page 77: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/77.jpg)
Реализация указателей опасности
77
// вернуть указатель опасности для текущего потокаstd::atomic<void*>& get_hazard_pointer_for_current_thread() { thread_local static hp_owner hazard; return hazard.get_pointer();}
// проверить, не ссылается ли на указатель какой-то из УОbool outstanding_hazard_pointers_for(void* p) { for (auto i = 0; i < max_hazard_pointers; i++) { if (hazard_pointers[i].pointer.load() == p) { return true; } } return false;}
![Page 78: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/78.jpg)
Реализация функции освобождения памяти
78
template <typename T>void do_delete(void* p) { delete static_cast<T*>(p);}
struct data_to_reclaim { // обёртка над данными для
void* data; // помещения в список удаления
std::function<void(void*)> deleter;
data_to_reclaim* next;
template<typename T> data_to_reclaim(T* p): data{p}, deleter{&do_delete<T>}, next{0} { }
~data_to_reclaim() { deleter(data); }};
std::atomic<data_to_reclaim*> nodes_to_reclaim;
![Page 79: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/79.jpg)
Реализация функции освобождения памяти
79
// добавить элемент в список на удалениеvoid add_to_reclaim_list(data_to_reclaim* node) { node->next = nodes_to_reclaim.load();
while (!nodes_to_reclaim.compare_exchange_weak( node->next, node));}
// удалить элемент позжеtemplate<typename T>void reclaim_later(T* data) { add_to_recalim_list(new data_to_reclaim(data));}
![Page 80: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/80.jpg)
Реализация функции освобождения памяти
80
void delete_nodes_with_no_hazards() { // захватить текущий список data_to_reclaim* current = nodes_to_reclaim.exchange(nullptr);
while (current) { data_to_reclaim* const next = current->next;
if (!outstanding_hazard_pointers_for(current->data)) // если не опасно, удалить сейчас delete current; else // если опасно удалить потом add_to_reclaim_list(current);
current = next; }}
![Page 81: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/81.jpg)
Реализация функции освобождения памяти
81
4321
nodes_to_reclaim
![Page 82: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/82.jpg)
Реализация функции освобождения памяти
82
nodes_to_reclaim
4321current
current = nodes_to_reclaim. exchange(nullptr);
![Page 83: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/83.jpg)
Реализация функции освобождения памяти
83
nodes_to_reclaim
432current
1
1
add_to_reclaim_list(current);
![Page 84: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/84.jpg)
Реализация функции освобождения памяти
84
nodes_to_reclaim
432current
1
delete current;
![Page 85: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/85.jpg)
Реализация функции освобождения памяти
85
nodes_to_reclaim
43current
1 5
add_to_reclaim_list() при выполнении pop()
![Page 86: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/86.jpg)
Реализация функции освобождения памяти
86
nodes_to_reclaim
4current
1 5 3
add_to_reclaim_list(current);
1
![Page 87: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/87.jpg)
Реализация функции освобождения памяти
87
nodes_to_reclaim
4current
1 5 3
![Page 88: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/88.jpg)
Реализация функции освобождения памяти
88
nodes_to_reclaim
current
1 5 3
![Page 89: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/89.jpg)
Недостатки указетелей опасности
89
1. Просмотр массива указателей опаности требует в худшем случае max_hazard_pointers атомарных переменных.
2. Атомарные операции могут работать медленнее эквивалентных обычных операций
3. При освобождении узла также требуется просмотреть список указателей опаности, т.е. max_hazard_pointers в худшем случае.
Функция pop дорогостоящая. Решения?
![Page 90: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/90.jpg)
Недостатки указетелей опасности
90
1. Просмотр массива указателей опаности требует в худшем случае max_hazard_pointers атомарных переменных.
2. Атомарные операции могут работать медленнее эквивалентных обычных операций
3. При освобождении узла также требуется просмотреть список указателей опаности, т.е. max_hazard_pointers в худшем случае.
Функция pop дорогостоящая. Решения?
1. Вместо просмотра max_hazard_pointers в каждом pop(), проверяем 2 * max_hazard_pointers через каждые max_hazard_pointers вызовов pop() и освобождаем не менее max_hazard_pointers. В среднем проверяем два узла при каждом вызове pop() и один освобождаем.
2. Каждый поток хранит собственный список освобождения в локальных данных потока. Это потребует выделения памяти под max_hazard_pointers2 узлов.
![Page 91: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/91.jpg)
Реализация стека, свободного от блокировок, с помощью умного указателя
91
![Page 92: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/92.jpg)
Реализация на основе атомарного умного указателя
92
▪ Удалять узлы можно только при отсутствии обращения к ним из других потоков
▪ Если на узел нет ссылки, то его можно удаляь
![Page 93: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/93.jpg)
Реализация на основе атомарного умного указателя
93
▪ Удалять узлы можно только при отсутствии обращения к ним из других потоков
▪ Если на узел нет ссылки, то его можно удаляь
▪ Умный указатель shared_ptr как раз решает эту задачу!
![Page 94: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/94.jpg)
Реализация на основе атомарного умного указателя
94
▪ Удалять узлы можно только при отсутствии обращения к ним из других потоков
▪ Если на узел нет ссылки, то его можно удаляь
▪ Умный указатель shared_ptr как раз решает эту задачу!
...
▪ Но, к сожалению, атомарные операции shared_ptr в большинстве реализаций не свободны от блокировок.
![Page 95: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/95.jpg)
Реализация на основе атомарного умного указателя
95
template <typename T>class lfstack {private:
struct node { std::shared_ptr<T> data; std::shared_ptr<node> next; node(T const& _data): data(std::make_shared<T>(_data)) { } };
std::shared_ptr<node> head;
![Page 96: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/96.jpg)
Реализация на основе атомарного умного указателя
96
...
void push(T const& data) { std::shared_ptr<node> const new_node = std::make_shared<node>(data); new_node->next = head.load();
while (!std::atomic_compare_exchange_weak(&head, &nead_node->next, new_node)); }
std::shared_ptr<T> pop() { std::shared_ptr<node> old_head = std::atomic_load(&head);
while (old_head && !std::atomic_compare_exchange_weak(&head, &old_head, old_head->next));
return old_head ? old_head->data : std::shared_ptr<T>(); }
![Page 97: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/97.jpg)
Реализация стека, свободного от блокировок, с помощью подсчёта ссылок
97
![Page 98: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/98.jpg)
Двойной счётчик ссылок
98
counted_node_ptr
node
internal_count
data
next
external_count
▪ При начале каждого чтения внешний счётчик увеличивается.
▪ При завершении чтения внутренний счётчик уменьшается.
▪ При удалении узла внутренний счетчик увеличивается на величину внешнего минус 1, а внешний отбрасывается.
▪ Если внутренний счётчик равен 0, узел можно удалять.
![Page 99: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/99.jpg)
Двойной счётчик ссылок
99
head
1 2 3
![Page 100: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/100.jpg)
Двойной счётчик ссылок
100
template<typename T>class lfstack {private:
struct counted_node_ptr { int external_count; node* ptr; };
struct node { std::shared_ptr<T> data; std::atomic<int> internal_count; counted_node_ptr next; node(T const& _data): data(std::make_shared<T>(_data)), internal_count(0) {} };
std::atomic<counted_node_ptr> head;
![Page 101: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/101.jpg)
Двойной счётчик ссылок
101
~lfstack() { while (pop()); }
void push(T const& data) { counted_node_ptr new_node; new_node.ptr = new node(data); new_node.external_count = 1; new_node.ptr->next = head.load(); while (!head.compare_exchange_weak(new_node.ptr->next, new_node)); }};
![Page 102: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/102.jpg)
Двойной счётчик ссылок
102
template <typename T>class lfstack {private:
// Увеличение внешнего счётчика void increase_head_count(counted_node_ptr& old_counter) { counted_node_ptr new_counter;
do { new_counter = old_counter; ++new_counter.external_count; } while (!head.compare_exchange_strong(old_counter, new_counter));
old_counter.external_count = new_counter.external_count; }
![Page 103: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/103.jpg)
public: std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load();
for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) delete ptr; return res;
} else if (ptr->internal_count.fetch_sub(1) == 1) delete ptr; } }
Двойной счётчик ссылок
103
![Page 104: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/104.jpg)
public: std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load();
for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) delete ptr; return res;
} else if (ptr->internal_count.fetch_sub(1) == 1) delete ptr; } }
Двойной счётчик ссылок
104
1. Увеличить внешний счётчик2. Разыменовать указатель3. Проверить указатель на пустоту
![Page 105: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/105.jpg)
public: std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load();
for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) delete ptr; return res;
} else if (ptr->internal_count.fetch_sub(1) == 1) delete ptr; } }
Двойной счётчик ссылок
105
Попытаться выполнить удаление узла
1. Если получилось, забрать данные2. Прибавить внутренний счётчик к внешнему3. Если счётчик стал равным 0, удалить узел4. Вернуть результат (даже если счётчик не стал равным 0)
![Page 106: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/106.jpg)
public: std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load();
for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) delete ptr; return res;
} else if (ptr->internal_count.fetch_sub(1) == 1) delete ptr; } }
Двойной счётчик ссылок
106
Если не получилось выполнить удаление узла (какой-то поток удалил узел раньше нас)1. Уменьшить счётчик ссылок на 1.2. Если другие потоки на узел не ссылаются, освободить память
(убрать за тем потоком, который выполнил удаление)
![Page 107: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/107.jpg)
0 0
Двойной счётчик ссылок
107
1
0
1 1
head
1 2 3
Сценарий 1:
Поток А эксклюзивно удаляет узел.
Другие потоки ему не мешают.
![Page 108: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/108.jpg)
0 0
Двойной счётчик ссылок
108
2 1 1
head
1 2 3
Поток A:increase_head_count(old_head)node* const = old_head.ptr
0
![Page 109: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/109.jpg)
0 0 0
Двойной счётчик ссылок
109
2 1 1
head
1 2 3
Поток A:head.compare_exchange(old_head, ptr->next)
![Page 110: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/110.jpg)
0 0 0
Двойной счётчик ссылок
110
2 0 0
head
1 2 3
Поток A:count_increase = 2 - 2 = 0internal_count = 0 + 0 = 0
![Page 111: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/111.jpg)
0 0 0
Двойной счётчик ссылок
111
2 0 0
head
1 2 3
Поток A:delete ptr
![Page 112: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/112.jpg)
0 0 0
Двойной счётчик ссылок
112
1 1 1
head
1 2 3
Сценарий 2:
Потоки А и В одновременно удаляют узел.
Потоку А удаётся выполнить удаление узла вперёд B.
Поток В успевает выйти из pop до того,
как А попробует освободить узел.
![Page 113: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/113.jpg)
0 0 0
Двойной счётчик ссылок
113
2 1 1
head
1 2 3
Поток B: increase_head_count(old_head)
![Page 114: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/114.jpg)
0 0 0
Двойной счётчик ссылок
114
3 1 1
head
1 2 3
Поток B: increase_head_count(old_head)
Поток A: increase_head_count(old_head) head.compare_exchange(...)
![Page 115: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/115.jpg)
-1 0 0
Двойной счётчик ссылок
115
3 0 0
head
1 2 3
Поток B: increase_head_count(old_head)
Поток B: internal_count.fetch_sub(1)
Поток A: increase_head_count(old_head) head.compare_exchange(...)
![Page 116: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/116.jpg)
0 0 0
Двойной счётчик ссылок
116
x 0 0
head
1 2 3
Поток A: count_increase = 3 - 2 = 1 internal_count = -1 + 1 = 0
Поток B: increase_head_count(old_head)
Поток B: internal_count = 0 - 1 = -1
Поток A: increase_head_count(old_head) head.compare_exchange(...)
![Page 117: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/117.jpg)
0 0 0
Двойной счётчик ссылок
117
x 0 0
head
1 2 3
Поток A: count_increase = 3 - 2 = 1 internal_count = -1 + 1 = 0
Поток B: increase_head_count(old_head)
Поток B: internal_count = 0 - 1 = -1
Поток A: increase_head_count(old_head) head.compare_exchange(...)
Поток A: delete ptr
![Page 118: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/118.jpg)
0 0 0
Двойной счётчик ссылок
118
1 1 1
head
1 2 3
Сценарий 3:
Потоки А и В одновременно удаляют узел.
Потоку А удаётся выполнить удаление узла вперёд B.
Поток В не успевает выйти из pop, когда А пытается
освободить узел, и поэтому А узел не освобождает.
Зато поток В, последним покидая узел,
освобождает память из-под узла, удалённого А.
![Page 119: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/119.jpg)
0 0 0
Двойной счётчик ссылок
119
3 1 1
head
1 2 3
Поток B: increase_head_count(old_head)
Поток A: increase_head_count(old_head) head.compare_exchange(...)
![Page 120: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/120.jpg)
1 0 0
Двойной счётчик ссылок
120
x 1 1
head
1 2 3
Поток B: increase_head_count(old_head)
Поток A: increase_head_count(old_head) head.compare_exchange(...)
Поток A: count_increase = 3 - 2 = 1 internal_count = 0 + 1 = 1
Поток A узел не освобождает
![Page 121: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/121.jpg)
0 0 0
Двойной счётчик ссылок
121
x 1 1
head
1 2 3
Поток B: increase_head_count(old_head)
Поток A: increase_head_count(old_head) head.compare_exchange(...)
Поток A: count_increase = 3 - 2 = 1 internal_count = 0 + 1 = 1
Поток A узел не освобождает
Поток B: internal_count = 1 - 1 = 0 delete ptr
Узел освобождает поток B
![Page 122: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/122.jpg)
Двойной счётчик ссылок - проблема
122
template<typename T>class lfstack {private:
struct counted_node_ptr { int external_count; node* ptr; };
struct node { std::shared_ptr<T> data; std::atomic<int> internal_count; counted_node_ptr next; node(T const& _data): data(std::make_shared<T>(_data)), internal_count(0) {} };
std::atomic<counted_node_ptr> head;
Структура может не поддерживать выполнение атомарных операций без блокировок!
![Page 123: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/123.jpg)
Применение модели памяти С++ для стека, свободного от блокировок
123
![Page 124: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/124.jpg)
Применение модели памяти С++
124
void push(T const& data) { counted_node_ptr new_node; new_node.ptr = new node(data); new_node.external_count = 1; new_node.ptr->next = head.load(std::memory_order_relaxed);
while (!head.compare_exchange_weak(new_node.ptr->next, new_node));
void increase_head_count(counted_node_ptr& old_counter) { counted_node_ptr new_counter;
do { new_counter = old_counter; ++new_counter.external_count; } while (!head.compare_exchange_strong(old_counter, new_counter));
old_counter.external_count = new_counter.external_count;}
![Page 125: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/125.jpg)
Применение модели памяти С++
125
void push(T const& data) { counted_node_ptr new_node; new_node.ptr = new node(data); new_node.external_count = 1; new_node.ptr->next = head.load(std::memory_order_relaxed);
while (!head.compare_exchange_weak(new_node.ptr->next, new_node));
void increase_head_count(counted_node_ptr& old_counter) { counted_node_ptr new_counter;
do { new_counter = old_counter; ++new_counter.external_count; } while (!head.compare_exchange_strong(old_counter, new_counter));
old_counter.external_count = new_counter.external_count;}
Подготовка данных
Установка head (“флага”)
Проверка head (“флага”)
Работа с добавленным элементом
![Page 126: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/126.jpg)
Применение модели памяти С++
126
void push(T const& data) { counted_node_ptr new_node; new_node.ptr = new node(data); new_node.external_count = 1; new_node.ptr->next = head.load(std::memory_order_relaxed);
while (!head.compare_exchange_weak(new_node.ptr->next, new_node, std::memory_order_release, std::memory_order_relaxed));
void increase_head_count(counted_node_ptr& old_counter) { counted_node_ptr new_counter;
do { new_counter = old_counter; ++new_counter.external_count; } while (!head.compare_exchange_strong(old_counter, std::memory_order_acquire, std::memory_order_relaxed, new_counter));
old_counter.external_count = new_counter.external_count;}
Подготовка данных
Установка head (“флага”)
Работа с добавленным элементом
Проверка head (“флага”)
![Page 127: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/127.jpg)
Применение модели памяти С++
127
std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load(); for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) delete ptr; return res;
} else if (ptr->internal_count.fetch_sub(1) == 1) delete ptr; }}
Чтение указателя
![Page 128: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/128.jpg)
Применение модели памяти С++
128
std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load(); for (;;) { increase_head_count(old_head);
node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next, std::memory_order_relaxed)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) delete ptr; return res;
} else if (ptr->internal_count.fetch_sub(1) == 1) delete ptr; }}
Чтение указателя
acquire не нужен, т.к. захват выполнен в
increase_head_count
![Page 129: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/129.jpg)
Применение модели памяти С++
129
std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load(); for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next, std::memory_order_relaxed)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) delete ptr; return res;
} else if (ptr->internal_count.fetch_sub(1) == 1) delete ptr; }}
Извлечение данных
Удаление должно выполняться после извлечения данных
![Page 130: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/130.jpg)
Применение модели памяти С++
130
std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load(); for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next, std::memory_order_relaxed)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase, std::memory_order_release) == -count_increase) delete ptr; return res;
} else if (ptr->internal_count.fetch_sub(1) == 1) delete ptr; }}
Удаление должно выполняться после извлечения данных
Извлечение данных
![Page 131: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/131.jpg)
Применение модели памяти С++
131
std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load(); for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next, std::memory_order_relaxed)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase, std::memory_order_release) == -count_increase) delete ptr; return res; } else if (ptr->internal_count.fetch_sub(1, std::memory_order_acquire) == 1)
delete ptr; }
Удаление должно выполняться после извлечения данных
Извлечение данных
![Page 132: ПВТ - осень 2014 - Лекция 7. Многопоточное программирование без блокировок. Модель потребитель-производитель.](https://reader034.fdocument.pub/reader034/viewer/2022042701/55b058b01a28abc02a8b45d6/html5/thumbnails/132.jpg)
Применение модели памяти С++
132
std::shared_ptr<T> pop() { counted_node_ptr old_head = head.load(); for (;;) { increase_head_count(old_head); node* const ptr = old_head.ptr; if (!ptr) return std::shared_ptr<T>();
if (head.compare_exchange_strong(old_head, ptr->next, std::memory_order_relaxed)) { std::shared_ptr<T> res; res.swap(ptr->data); int const count_increase = old_head.external_count - 2;
if (ptr->internal_count.fetch_add(count_increase, std::memory_order_release) == -count_increase) delete ptr; return res; } else if (ptr->internal_count.fetch_sub(1, std::memory_order_relaxed) == 1) ptr->internal_count.load(std::memory_order_acquire);
delete ptr; }
Достаточно вставить операцию захвата-загрузки, чтобы удалить ptr после извлечения
данных
Извлечение данных
fetch_sub входит в последовательность
освобождений, поэтому “не мешает” acquire