Заблокирован
|
||||||
1 | ||||||
Атомарна ли операция присваивания и чтения POD данных?29.05.2013, 11:43. Показов 1498. Ответов 10
Метки нет Все метки)
(
Привет! Терзают меня сомнения по поводу необходимости критической секции в следующие ситуации:
Есть класс, в нём есть поля простых типов данных, например:
Вот интересно, нужна ли тут критическая секция, ведь на сколько мне кажется, операции записи и присваивания простых данных - атомарны, тоесть выполняются за один такт процессора, ну тоесть просто в один регистр процессора записываются или оттуда же читаются. Тоесть в принципе в однопроцессорной машине думаю проблем быть не должно без критических секций, т.к. эти операции не могут одновременно как бы выполняться, так? А в многопроцессорной машине? А в однопроцессорной, но многоядерной? А если среди данных присутствует std::string ? тоесть как бы один поток стринг эстелевский пишет, а другой читает?
0
|
|
29.05.2013, 11:43 | |
Ответы с готовыми решениями:
10
Атомарна ли операция битового сдвига? Операция присваивания Операция присваивания (=) |
![]() 2924 / 1274 / 114
Регистрация: 27.05.2008
Сообщений: 3,465
|
|
29.05.2013, 12:52 | 3 |
Нельзя закладываться на якобы атомарность операций. Потому что оптимизирующий компилятор может так перемешать ассемблерные инструкции, что ой-ей-ей!
0
|
![]() 2924 / 1274 / 114
Регистрация: 27.05.2008
Сообщений: 3,465
|
|
29.05.2013, 14:46 | 5 |
Не всегда это нужно. В Win API, например, есть семейство Interlocked..()-функций. Вот они гарантируют атомарность операции.
1
|
Ушел с форума
![]() |
|
30.05.2013, 13:34 | 7 |
На архитектурах IA-32 и AMD64 операции чтения и записи в память атомарны для
типов BYTE (1 байт), WORD (слово, 2 байта), DWORD (двойное слово, 4 байта) и QWORD (четверное слово, 8 байт), последнее только в 64-битном режиме работы процессора (Long Mode). При условии, что данные корректно выровнены или находятся в пределах одной кэш-линии. Некоторые встроенные типы языков C и C++, такие как char, int, указатель и др., естественным образом отображаются на типы BYTE/WORD/DWORD/QWORD, поэтому их можно смело читать и писать из разных потоков, не боясь получить промежуточное значение. Современные компиляторы достаточно умны и не станут разбивать, скажем, запись в WORD-переменную на две BYTE-операции. Фактически, все сведется к инструкции mov. Выравнивание данных обеспечивается как компилятором, так и системой: стековые переменные и члены структур выравниваются компилятором, при выделении памяти с помощью malloc/new/VirtualAlloc и т.п. аллокаторы выдают уже выровненные адреса. Все это справедливо исключительно для примитивных операций, которые на уровне машинных команд реализуются в виде "memory-register" или "register-memory". Операции класса "read-modify-update", такие как инкремент, уже не будут атомарными. Компилятор может реализовать инкремент как "mov, add 1, mov". Да и инструкция inc сама по себе тоже не является атомарной, так как реализуется через микрокод. В этих случаях необходимо пользоваться всякими <atomic> и Interlocked-функциями. Но это все атомарность на уровне CPU, а есть еще логическая атомарность, которая приобретает смысл в контексте определенных идей, заложенных программистом. Например, класс динамического массива, хранящий указатель на буфер памяти и поля num_items и capacity, по определению должен рассматриваться, как неделимое целое: ни в один момент времени не должно быть несогласованности между значениями полей. То, что мы можем сначала атомарно обновить одно поле, потом атомарно второе и так далее, в данном случае нам ничем не поможет, атомарной должна быть вся операция обновления класса. Вот здесь и нужны такие вещи, как критические секции, спин- блокировки, мьютексы и т.п. Правила атомарности одинаковы для любых топологий - однопроцессорные, многопроцессорные, многоядерные, многоядерные с Hyper-Threading и т.д. Они принципиально не менялись еще со времен Intel 486. Зависит от реализации STL. Например, STL-классы в Visual C++ потокобезопасны только на чтение. Множество потоков могут читать данные из них, но если есть хотя бы один пишущий, то нужна синхронизация. Кстати, необязательно лепить везде критические секции, можно использовать "readers-writers locks", спин-блокировки, lock-free и т.п. Если перемешать две ассемблерные инструкции mov, атомарными от этого они быть не перестанут. А если важна видимость данных и их порядок, тогда следует применять volatile, компиляторные барьеры и барьеры памяти.
1
|
Заблокирован
|
|
25.09.2013, 21:17 | 8 |
Подниму ка я старую тему.
Убежденный, исходя из выше сказанного, осмелюсь предположить, что если я определяю в глобальном пространстве переменную типа BYTE, WORD, DWORD или QWORD, то есть объявлю её статический на этапе компиляции, а не динамический при помощи new, то она будет корректно выровнена и будет находится в пределах одной кэш линии, в случае её записи и чтения из кучи других потоков? ![]()
0
|
Ушел с форума
![]() |
|
25.09.2013, 21:49 | 9 |
В стандарте языка С++ описана такая вещь, как "alignment requirements" -
требования к выравниванию данных. Данные выравниваются по адресам, кратным размеру этих данных. Например, int размещается по адресу, кратному 4 байтам, потому что sizeof (int) == 4. То есть, глобальная ли переменная, локальная, статическая - не важно, она все равно будет правильно выровнена компилятором. Кстати, new тоже имеет определенные гарантии выравнивания.
1
|
Заблокирован
|
|
25.09.2013, 22:04 | 10 |
Большое спасибо! Теперь буду смело перезаписывать BYTE, WORD, DWORD, QWORD, char, int из разных потоков без синхронизации...
Единственное что осталось уточнить. Если я аллоцирую память под массив, например DWORD test = new DWORD[10], будут ли элементы массива так сказать придерживаться coalesced memory, тоесть идти в памяти друг за другом по порядку по умолчанию? А если статический DWORD test[10]; ? И если я к 10-и этим элементам из разных 10-и потоков буду "одновременно" обращаться придерживаясь правила - что первый поток обращается только к 1-му элементу массива, второй поток - только ко второму элементу массива и тд, нормально ли это будет работать без не предвиденного результата?
0
|
Ушел с форума
![]() |
|
25.09.2013, 22:11 | 11 |
Это справедливо и для других типов данных размера большего, чем int - в 32-битных
процессах доступ к ним не может быть атомарным без использования внешних средств синхронизации.
1
|
25.09.2013, 22:11 | |
Помогаю со студенческими работами здесь
11
Операция присваивания операция присваивания Преобразование типов в операция присваивания Конструктор копирования и операция присваивания в классе Искать еще темы с ответами Или воспользуйтесь поиском по форуму: |