Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
- Название:Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-448-1
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ краткое содержание
Книга «Параллельное программирование на С++ в действии» не предполагает предварительных знаний в этой области. Вдумчиво читая ее, вы научитесь писать надежные и элегантные многопоточные программы на С++11. Вы узнаете о том, что такое потоковая модель памяти, и о том, какие средства поддержки многопоточности, в том числе запуска и синхронизации потоков, имеются в стандартной библиотеке. Попутно вы познакомитесь с различными нетривиальными проблемами программирования в условиях параллелизма.
Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
Отметим, что несмотря на то, что типы std::atomicи std::atomicформально разрешены, так как встроенные типы с плавающей точкой удовлетворяют сформулированным выше критериям на использование memcpyи memcmp, их поведение в части функции compare_exchange_strongможет оказаться неожиданным. Операция может завершиться отказом, даже если ранее сохраненное значение численно равно ожидаемому, но имеет другое внутреннее представление. Отметим также, что над числами с плавающей точкой не определены атомарные арифметические операции. Аналогичное поведение compare_exchange_strongвы получите, если конкретизируете std::atomic<>пользовательским типом, в котором оператор сравнения на равенство определён, но отличается от сравнения с помощью memcmp— операция может завершиться отказом, потому что равные значения имеют различное представление.
Если размер пользовательского типа UDTравен (или меньше) размеру intили void*, то на большинстве платформ для типа std::atomicможно сгенерировать код, содержащий только атомарные команды. На некоторых платформах подобный код можно сгенерировать и в случае, когда размер пользовательского типа в два раза превышает размер intили void*. Обычно это платформы, на которых имеется команда сравнения и обмена двойных слов double-word-compare-and-swap (DWCAS) , соответствующая функциям compare_exchange_xxx.
В главе 7 мы увидим, что такая поддержка может быть полезна для написания кода без блокировок. В силу описанных ограничений вы не можете создать, к примеру, тип std::atomic>, но можете использовать для параметризации классы, содержащие счетчики, флаги, указатели и даже массивы простых элементов. Обычно это не проблема; чем сложнее структура данных, тем больше вероятность, что в ней нужно будет определить какие-то другие операции, помимо простейшего присваивания и сравнения. Но в таком случае лучше воспользоваться классом std::mutex, который гарантирует надлежащую защиту данных при выполнении этих операций (см. главу 3).
Интерфейс шаблона std::atomic, конкретизированного пользовательским типом T, ограничен набором операций, доступных классу std::atomic: load(), store(), exchange(), compare_exchange_weak(), compare_exchange_strong(), присваивание значения типа Tи преобразование в значение типа T.
В табл. 5.3 перечислены операции, доступные для всех атомарных типов.
Таблица 5.3.Операции над атомарными типами
| Операция | atomic_ flag |
atomic |
atomic |
atomic |
atomic |
|---|---|---|---|---|---|
test_and_set |
√ | ||||
clear |
√ | ||||
is_lock_free |
√ | √ | √ | √ | |
load |
√ | √ | √ | √ | |
store |
√ | √ | √ | √ | |
exchange |
√ | √ | √ | √ | |
compare_exchange_weak, compare_exchange_strong |
√ | √ | √ | √ | |
fetch_add, += |
√ | √ | |||
fetch_sub, -= |
√ | √ | |||
fetch_or, |= |
√ | ||||
fetch_and, &= |
√ | ||||
fetch_xor, ^= |
√ | ||||
++, -- |
√ | √ |
5.2.7. Свободные функции для атомарных операций
До сих пор я описывал только те операции над атомарными типами, которые реализованы функциями-членами. Однако для всех этих операций существуют также эквивалентные функции, не являющиеся членами классов. Как правило, имена свободных функций строятся по единому образцу: имя соответствующей функции-члена с префиксом atomic_(например, std::atomic_load()). Затем эти функции перегружаются для каждого атомарного типа. Если имеется возможность задать признак упорядочения доступа к памяти, то предлагаются две разновидности функции: одна без признака, другая — ее имя заканчивается суффиксом _explicit— с одним или несколькими дополнительными параметрами для задания признаков (например, std::atomic_store(&atomic_var, new_value)и std::atomic_store_explicit(&atomic_var, new_value, std::memory_order_release). Если в случае функций-членов объект атомарного типа задается неявно, то все свободные функции принимают в первом параметре указатель на такой объект.
Например, для функции std::atomic_is_lock_free()есть только одна разновидность (хотя и перегруженная для всех типов), причём std::atomic_is_lock_free(&a)возвращает то же значение, что a.is_lock_free()для объекта аатомарного типа. Аналогично std::atomic_load(&a)— то же самое, что a.load(), а эквивалентом a.load(std::memory_order_acquire)является std::atomic_load_explicit(&a, std::memory_order_acquire).
Свободные функции совместимы с языком С, то есть во всех случаях принимают указатели, а не ссылки. Например, первый параметр функций-членов compare_exchange_weak()и compare_exchange_strong()(ожидаемое значение) — ссылка, но вторым параметром std::atomic_compare_exchange_weak()(первый — это указатель на объект) является указатель. Функция std::atomic_compare_exchange_weak_explicit()также требует задания двух параметров, определяющих упорядочение доступа к памяти в случае успеха и отказа, тогда как функции-члены для сравнения с обменом имеют варианты как с одним параметром (второй по умолчанию равен std::memory_order_seq_cst), так и с двумя.
Операции над типом std::atomic_flagнарушают традицию, поскольку в именах функций присутствует дополнительное слово «flag»: std::atomic_flag_test_and_set(), std::atomic_flag_clear(), но у вариантов с параметрами, задающими упорядочение доступа, суффикс _explicitпо-прежнему имеется: std::atomic_flag_test_and_set_explicit()и std::atomic_flag_clear_explicit().
В стандартной библиотеке С++ имеются также свободные функции для атомарного доступа к экземплярам типа std::shared_ptr<>. Это отход от принципа, согласно которому атомарные операции поддерживаются только для атомарных типов, поскольку тип std::shared_ptr<>заведомо не атомарный. Однако комитет по стандартизации С++ счел этот случай достаточно важным, чтобы предоставить дополнительные функции. К числу определенных для него атомарных операций относятся загрузка , сохранение , обмен и сравнение с обменом , и реализованы они в виде перегрузок тех же операций над стандартными атомарными типами, в которых первым аргументом является указатель std::shared_ptr<>*:
std::shared_ptr p;
void process_global_data() {
std::shared_ptr local = std::atomic_load(&p);
process_data(local);
}
void update_global_data() {
std::shared_ptr local(new my_data);
std::atomic_store(&p, local);
}
Как и для атомарных операций над другими типами, предоставляются _explicit-варианты, позволяющие задать необходимое упорядочение, а для проверки того, используется ли в реализации внутренняя блокировка, имеется функция std::atomic_is_lock_free().
Интервал:
Закладка: