Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
- Название:Параллельное программирование на С++ в действии. Практика разработки многопоточных программ
- Автор:
- Жанр:
- Издательство:ДМК Пресс
- Год:2012
- Город:Москва
- ISBN:978-5-94074-448-1
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Энтони Уильямс - Параллельное программирование на С++ в действии. Практика разработки многопоточных программ краткое содержание
Книга «Параллельное программирование на С++ в действии» не предполагает предварительных знаний в этой области. Вдумчиво читая ее, вы научитесь писать надежные и элегантные многопоточные программы на С++11. Вы узнаете о том, что такое потоковая модель памяти, и о том, какие средства поддержки многопоточности, в том числе запуска и синхронизации потоков, имеются в стандартной библиотеке. Попутно вы познакомитесь с различными нетривиальными проблемами программирования в условиях параллелизма.
Параллельное программирование на С++ в действии. Практика разработки многопоточных программ - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
В С++ для того чтобы операция была атомарной, обычно необходимы атомарные типы. Давайте познакомимся с ними.
5.2.1. Стандартные атомарные типы
Все стандартные атомарные типы определены в заголовке . Любые операции над такими типами атомарны, и только операции над этими типами атомарны в смысле принятого в языке определения, хотя мьютексы позволяют реализовать кажущуюся атомарность других операций. На самом деле, и сами стандартные атомарные типы могут пользоваться такой эмуляцией: почти во всех имеется функция-член is_lock_free()
, которая позволяет пользователю узнать, выполняются ли операции над данным типом с помощью действительно атомарных команд ( x.is_lock_free()
возвращает true
) или с применением некоторой внутренней для компилятора и библиотеки блокировки ( x.is_lock_free()
возвращает false
).
Единственный тип, в котором функция-член is_lock_free()
отсутствует, — это std::atomic_flag
. В действительности это по-настоящему простой булевский флаг, а операции над этим типом обязаны быть свободными от блокировок; если имеется простой свободный от блокировок булевский флаг, то на его основе можно реализовать простую блокировку и, значит, все остальные атомарные типы. Говоря по-настоящему простой , я именно это и имел в виду: после инициализации объект типа std::atomic_flag
сброшен, и для него определены всего две операции: проверить и установить (функция-член test_and_set()
) и очистить (функция-член clear()
). Это всё — нет ни присваивания, ни копирующего конструктора, ни операции «проверить и очистить», вообще ничего больше.
Доступ ко всем остальным атомарным типам производится с помощью специализаций шаблона класса std::atomic<>
; их функциональность несколько богаче, но они необязательно свободны от блокировок (как было объяснено выше). На самых распространенных платформах можно ожидать, что атомарные варианты всех встроенных типов (например, std::atomic
и std::atomic
) действительно будут свободны от блокировок, но такого требования не предъявляется. Как мы скоро увидим, интерфейс каждой специализации отражает свойства типа; например, поразрядные операции, например &=
, не определены для простых указателей, поэтому они не определены и для атомарных указателей.
Помимо прямого использования шаблона класса std::atomic<>
, разрешается использовать имена, приведённые в табл. 5.1, которые ссылаются на определенные в конкретной реализации атомарные типы. Из-за исторических особенностей добавления атомарных типов в стандарт С++ альтернативные имена типов могут ссылаться либо на соответствующую специализацию std::atomic<>
, либо на базовый класс этой специализации. Поэтому смешение альтернативных имен и прямых имен специализаций std::atomic<>
может сделать программу непереносимой.
Таблица 5.1.Альтернативные имена стандартных атомарных типов и соответствующие им специализации std::atomic<>
Атомарный тип | Соответствующая специализация |
---|---|
atomic_bool |
std::atomic |
atomic_char |
std::atomic |
atomic_schar |
std::atomic |
atomic_uhar |
std::atomic |
atomic_int |
std::atomic |
atomic_uint |
std::atomic |
atomic_short |
std::atomic |
atomic_ushort |
std::atomic |
atomic_long |
std::atomic |
atomic_ulong |
std::atomic |
atomic_llong |
std::atomic |
atomic_ullong |
std::atomic |
atomic_char16_t |
std::atomic |
atomic_char32_t |
std::atomic |
atomic_wchar_t |
std::atomic |
Помимо основных атомарных типов, в стандартной библиотеке С++ определены также псевдонимы typedef
для атомарных типов, соответствующих различным неатомарным библиотечным typedef
, например std::size_t
. Они перечислены в табл. 5.2.
Таблица 5.2.Соответствие между стандартными атомарными и встроенными typedef
Атомарный typedef |
Соответствующий typedef из стандартной библиотеки |
---|---|
atomic_int_least8_t |
int_least8_t |
atomic_uint_least8_t |
uint_least8_t |
atomic_int_least16_t |
int_least16_t |
atomic_uint_least16_t |
uint_least16_t |
atomic_int_least32_t |
int_least32_t |
atomic_uint_least32_t |
uint_least32_t |
atomic_int_least64_t |
int_least64_t |
atomic_uint_least64_t |
uint_least64_t |
atomic_int_fast8_t |
int_fast8_t |
atomic_uint_fast8_t |
uint_fast8_t |
atomic_int_fast16_t |
int_fast16_t |
atomic_uint_fast16_t |
uint_fast16_t |
atomic_int_fast32_t |
int_fast32_t |
atomic_uint_fast32_t |
uint_fast32_t |
atomic_int_fast64_t |
int_fast64_t |
atomic_uint_fast64_t |
uint_fast64_t |
atomic_intptr_t |
intptr_t |
atomic_uintptr_t |
uintptr_t |
atomic_size_t |
size_t |
atomic_ptrdiff_t |
ptrdiff_t |
atomic_intmax_t |
intmax_t |
atomic_uintmax_t |
uintmax_t |
Да уж, типов немало! Но есть простая закономерность — атомарный тип, соответствующий стандартному typedef T
, имеет такое же имя с префиксом atomic_
: atomic_T
. То же самое относится и к встроенным типам с тем исключением, что signed
сокращается до s
, unsigned
— до u
, a long long
— до llong
. Вообще говоря, проще написать std::atomic
для нужного вам типа T
, чем пользоваться альтернативными именами.
Стандартные атомарные типы не допускают копирования и присваивания в обычном смысле, то есть не имеют копирующих конструкторов и операторов присваивания. Однако им все же можно присваивать значения соответствующих встроенных типов, и они поддерживают неявные преобразования в соответствующие встроенные типы. Кроме того, в них определены функции-члены load()
, store()
, exchange()
, compare_exchange_weak()
и compare_exchange_strong()
. Поддерживаются также составные операторы присваивания (там, где это имеет смысл) +=
, -=
, *=
, |=
и т.д., а для целочисленных типов и специализаций std::atomic<>
для указателей — еще и операторы ++
и --
. Этим операторам соответствуют также именованные функции-члены с идентичной функциональностью: fetch_add()
, fetch_or()
и т.д. Операторы присваивания возвращают сохраненное значение, а именованные функции-члены — значение, которое объект имел до начала операции. Это позволяет избежать потенциальных проблем, связанных с тем, что обычно операторы присваивания возвращают ссылку на объект в левой части. Чтобы получить из такой ссылки сохраненное значение, программа должна была бы выполнить еще одну операцию чтения, но тогда между присваиванием и чтением другой поток мог бы модифицировать значение, открывая дорогу гонке.
Интервал:
Закладка: