
- •Национальный исследовательский ядерный университет «мифи»
- •Пояснительная записка к дипломному проекту на тему:
- •Глава 1. Обзор методов и средств многопоточного взаимодействия 7
- •Глава 2. Разработка структуры и алгоритмов взаимодействия 29
- •Глава 3. Реализация и тестирование разработанных структур и алгоритмов взаимодействия 53
- •Введение
- •Глава 1. Обзор методов и средств многопоточного взаимодействия
- •1.1. Блокирующая синхронизация
- •1.2. Неблокирующая синхронизация
- •1.2.1. Общие сведения
- •1.2.2. Принципы неблокирующих алгоритмов Узлы неизменяемого типа
- •Подмена указателей
- •Атомарные операции
- •Специальные методы управления памятью
- •1.2.3. Обзор специальных методов управления памятью Метод использования специальных тегов
- •Метод неблокирующего подсчета ссылок
- •Метод опасных указателей
- •1.2.4. Оценка эффективности методов
- •1.2.5. Типы алгоритмов для неблокирующей синхронизации
- •1.3. Выводы
- •Глава 2. Разработка структуры и алгоритмов взаимодействия
- •2.1. Требования к разрабатываемой структуре данных и обоснование выбранных методов реализации
- •2.2. Обзор существующих неблокирующих структур
- •2.3. Разработка структуры данных
- •2.4. Разработка алгоритмов
- •2.4.1. Алгоритм записи
- •2.4.2. Алгоритм чтения
- •Метод неблокирующего подсчёта ссылок
- •Метод опасных указателей
- •2.4.3. Алгоритм освобождения памяти
- •Метод неблокирующего подсчёта ссылок
- •Метод опасных указателей
- •2.4.4. Алгоритм добавления и удаления опасных указателей
- •Глава 3. Реализация и тестирование разработанных структур и алгоритмов взаимодействия
- •3.1. Особенности программной реализации
- •3.2. Тестирование разработанных алгоритмов
- •3.3. Тестирование разработанной структуры при многопоточном доступе
- •3.4. Сравнение структур по временным характеристикам
- •Заключение
- •Список литературы
1.2.5. Типы алгоритмов для неблокирующей синхронизации
В работах [2,10] приводятся описания трёх типов алгоритмов для неблокирующей синхронизации.
Wait-free
Wait-free алгоритмом или алгоритмом без ожидания называется такой алгоритм, который гарантирует, что каждая операция, выполняемая потоком согласно этому алгоритму, будет выполнена за определенное известное число шагов, не зависимо от других потоков.
Каждый поток продвигается вперед не зависимо от внешних факторов (конкуренции со стороны других потоков, факта блокирования других потоков). Это самая сильная гарантия. Обычно это означает, что используются только такие примитивы, как atomic_exchange, atomic_increment, atomic_fetch_and_add (InterlockedExchange, InterlockedIncrement, InterlockedExchangeAdd), и в алгоритме нет циклов, на выполнение которых могут повлиять другие потоки. Такие примитивы, как atomic_compare_exchange (InterlockedCompareExchange) обычно не используется, так как с ним обычно связан цикл вида «выполнять atomic_compare_exchange, пока он не будет выполнен успешно».
Пример wait-free алгоритма:
void increment_reference_counter(rc_base* obj)
{
atomic_increment(obj->rc);
}
void decrement_reference_counter(rc_base* obj)
{
if (0 == atomic_decrement(obj->rc))
delete obj;
}
Как видно, любой поток может выполнить эти функции за конечное число шагов, не зависимо ни от чего, однако нет никаких гарантий, что результатом выполнения данного алгоритма будут ожидаемые данные, так как используемые примитивы гарантируют отсутствие конфликтов между двумя вызовами, но не предотвращают возникновение некорректных данных.
Lock-free
Lock-free алгоритмом или алгоритмом без блокировок называется такой алгоритм, который гарантирует, что в течении времени, за которое некий программный поток выполнит некое известное ограниченное число шагов в операции над таким объектом или структурой, некий, возможно, другой поток обязательно завершит операцию над этим объектом или структурой.
Иными словами данный алгоритм гарантирует прогресс как минимум для одного потока. Другие потоки могут ждать, но один поток минимум должен прогрессировать. Система в целом продвигается вперед. Это более слабая гарантия, чем wait-free. Обычно используется примитив atomic_compare_exchange (InterlockedCompareExchange).
Пример lock-free алгоритма:
void stack_push(stack* s, node* n)
{
node* head;
do
{
head = s->head;
n->next = head;
}
while (!atomic_compare_exchange(s->head, head, n));
}
Как видно, любой поток может крутиться в этом цикле теоретически бесконечно. Но любой повтор этого цикла означает, что какой-то другой поток успешно выполнил свою операцию. И никакой заблокированный/прерванный поток не может препятствовать продвижению других потоков. Отсюда следует, что система в целом продвигается вперед не зависимо ни от чего. В отличие от wait-free алгоритмов, lock-free алгоритмы благодаря использованию примитивов в цикле не только гарантируют отсутствие конфликтов между вызовами, но и предотвращают возникновение некорректных данных.
Obstruction-free
Obstruction-free алгоритмом или алгоритмом без препятствий называется такой алгоритм, который гарантирует, что каждая операция, выполняемая согласно этому алгоритму, будет выполнена за определенное известное число шагов в том случае, если выполнение все конкурирующие потоков приостановлено.
Поток продвигается вперед, только если не встречает конкуренции со стороны других потоков. В частности это означает, что при сильной конкуренции возможно никакой поток не будет совершать продвижения. То есть система будет находиться в так называемом livelock. Это самая слабая гарантия. Этот класс алгоритмов может показаться немного странным, поэтому стоит заметить, что заблокированные/прерванные потоки не могут препятствовать прогрессу других потоков, и obstruction-free алгоритмы могут быть более быстрые, чем lock-free.