Russian Qt Forum
Ноябрь 05, 2024, 22:16 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Можно ли полагаться на атомарность присваивания/чтения int?  (Прочитано 8955 раз)
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« : Март 30, 2012, 23:39 »

В общем есть одна тян нитка, которая записывает значение int (только увеличивает) и несколько потоков, которые читают это значение.
Если было значение i1, а стало i2, то можно ли полагаться, что любая из читающих ниток получит i1 или i2, но никогда не получит, скажем, 2 байта от i1 и 2 байта от i2?
Или всё же придётся использовать мьютексы, локи или QAtomicInt?
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #1 : Март 30, 2012, 23:43 »

Я конечно не специалист во всех этих потока и т.д... Но наверное, QAtomicInt не просто так придумали)

Короче, я бы подстраховался)
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
V1KT0P
Гость
« Ответ #2 : Март 30, 2012, 23:44 »

В общем есть одна тян нитка, которая записывает значение int (только увеличивает) и несколько потоков, которые читают это значение.
Если было значение i1, а стало i2, то можно ли полагаться, что любая из читающих ниток получит i1 или i2, но никогда не получит, скажем, 2 байта от i1 и 2 байта от i2?
Или всё же придётся использовать мьютексы, локи или QAtomicInt?
Если использовалось выравнивание, то чтение и запись атомарны(только для int). Кстати посмотри список поддерживаемых и реализованных GCC 4.7 стандартов С++11 там много нового для облегчения многопоточного программирования, и я точно видел в списке фичу с использованием атомарных вещей.

добавлено:
Вот нашел для GCC начиная с 4.4: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html
« Последнее редактирование: Март 30, 2012, 23:47 от V1KT0P » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #3 : Март 31, 2012, 10:20 »

В общем есть одна тян нитка, которая записывает значение int (только увеличивает) и несколько потоков, которые читают это значение.
Если было значение i1, а стало i2, то можно ли полагаться, что любая из читающих ниток получит i1 или i2, но никогда не получит, скажем, 2 байта от i1 и 2 байта от i2?
Или всё же придётся использовать мьютексы, локи или QAtomicInt?
Нет, нельзя, сумма может оказаться некорректной (меньше верной) т.к. операции сложения и/или инкремента не атомарны. Мутекс неприемлемо дорог, QAtomicInt сам по себе еще не решает проблему. Используйте CAS
Код
C++ (Qt)
void MyAtomicAdd( QAtomicInt & count, int delta )
{
while (true) {
 int old = count;
 if (count.testAnSetAcquire(old, old + delta)) break;
}
}
 
Ну или где-то найти либы/ф-ции (это к эрудитам/знатокам)

Edit: тут конфуз с названием темы. На атомарность присваивания/чтения как раз полагаться можно, а вот на инкремент нельзя.
« Последнее редактирование: Март 31, 2012, 10:32 от Igors » Записан
LisandreL
Птица говорун
*****
Offline Offline

Сообщений: 984


Надо улыбаться


Просмотр профиля
« Ответ #4 : Март 31, 2012, 10:33 »

Используйте CAS
Код
C++ (Qt)
void MyAtomicAdd( QAtomicInt & count, int delta )
{
while (true) {
 int old = count;
 if (count.testAndSetAcquire(old, old + delta)) break;
}
}
 
Не очень понимаю применимость кода в моём случае. Добавлять (да и вообще модифицировать значение) в моём случае может только одна нитка. Так как же тогда testAnSet может не удаться?
А считывание значения из нескольких ниток как будет происходить? Просто
Код
C++ (Qt)
int countValue = count;
или надо что-нибудь вроде
Код
C++ (Qt)
int countValue = count.fetchAndAdd( 0 );
делать?
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #5 : Март 31, 2012, 10:48 »

Не очень понимаю применимость кода в моём случае. Добавлять (да и вообще модифицировать значение) в моём случае может только одна нитка. Так как же тогда testAnSet может не удаться?
А считывание значения из нескольких ниток как будет происходить?
Если категорическое утверждение "только одна модифицирует" - то никакие доп движения не нужны, работаете как с обычным int т.к. присваивание и считывание атомарны. Нужно только иметь ввиду что считанное значение хотя и корректно но может уже устареть, напр

Код
C++ (Qt)
if (count == 0)
printf("count = %d\n", count);
 
Так можете получить вывод "count = 1".

Обычно отделаться модификацией "только в одной" не удается  Улыбающийся
Записан
V1KT0P
Гость
« Ответ #6 : Март 31, 2012, 14:13 »

Нет, нельзя, сумма может оказаться некорректной (меньше верной) т.к. операции сложения и/или инкремента не атомарны.
Если у процессора есть особые инструкции, то и инкремент/декремент/сложение/вычитание можно сделать атомарными. При чем производительность почти не страдает.
Вот например список: "Атомарные инструкции x86" http://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%B0%D1%8F_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #7 : Март 31, 2012, 14:31 »

Если у процессора есть особые инструкции, то и инкремент/декремент/сложение/вычитание можно сделать атомарными.
А как Вы будете "делать"? Стоять со свечкой возле каждого + ? Улыбающийся Вставлять его в ассемблере? Полагаться на "этот" процессор/платформу/компилятор?

Расходы на атомарность могут быть видимы только при интенсивной конкуренции на атомарной переменной, в остальных случаях их часто даже не удается замерить. Нарыть набор атомарных ф-ций (или сделать самому хоть на том же QAtomicInt) - всех делов 10 мин.
Записан
V1KT0P
Гость
« Ответ #8 : Март 31, 2012, 14:36 »

Нарыть набор атомарных ф-ций (или сделать самому хоть на том же QAtomicInt) - всех делов 10 мин.
Да они уже давно есть в GCC : http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Atomic-Builtins.html.
Да и в MSVS я тоже где-то видел названия таких функций.
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #9 : Март 31, 2012, 14:57 »

Да они уже давно есть в GCC : http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Atomic-Builtins.html.
Там не наблюдается поддержки float/double. Неужели тогда придется "изобретать велосипед"?  Улыбающийся
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2095



Просмотр профиля
« Ответ #10 : Март 31, 2012, 15:12 »

Есть ещё std::atomic<T>
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Akon
Гость
« Ответ #11 : Март 31, 2012, 23:09 »

Цитировать
Если было значение i1, а стало i2, то можно ли полагаться, что любая из читающих ниток получит i1 или i2, но никогда не получит, скажем, 2 байта от i1 и 2 байта от i2?
Полагаться можно (по крайней мере в архитектуре x86/64). Инструкции ассемблера атомарны, независимо от выравнивания операндов. Невыровненный операнд просто снизит быстродействие за счет дополнительного шинного цикла чтения/записи.

В результате вы получите код вида
Код:
    mov [DestOfTypeInt], eax ; исходное значение берется из регистра

Если же компилятор сгенерирует такой код
Код:
    mov [DestOfTypeInt], ax ; исходное значение берется из регистра (16 бит)
    mov [DestOfTypeInt+2], dx ; исходное значение берется из регистра (16 бит)
то, очевидно, он неатомарен. Такой код генерируются для 16-ти битного кода. 32-х битный код генерируется как указано выше.

Переменная должна быть volatile
« Последнее редактирование: Март 31, 2012, 23:31 от Akon » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #12 : Апрель 01, 2012, 07:51 »

Переменная должна быть volatile
Желательно, рекомендуется все такие переменные объявлять volatile. Хотя в данном случае разницы нет
Записан
Akon
Гость
« Ответ #13 : Апрель 02, 2012, 07:50 »

Поток, который выполняет операцию 100 раз и пишет прогресс, читаемый другим потоком
Код:
    // progress - внешняя переменная
    ...
    for (progress = 0; progress < 100; ++progress) {
        someOperation();  // длится, к примеру, 1 секунду
        
        // что должно побудить компилятор на каждой итерации сохранять progress в памяти?
    }
    ...

В результате читающий поток увидит два значения - изначальное, а через 100 сек. - 100.
« Последнее редактирование: Апрель 02, 2012, 07:54 от Akon » Записан
V1KT0P
Гость
« Ответ #14 : Апрель 02, 2012, 08:10 »

Поток, который выполняет операцию 100 раз и пишет прогресс, читаемый другим потоком
Код:
    // progress - внешняя переменная
    ...
    for (progress = 0; progress < 100; ++progress) {
        someOperation();  // длится, к примеру, 1 секунду
        
        // что должно побудить компилятор на каждой итерации сохранять progress в памяти?
    }
    ...

В результате читающий поток увидит два значения - изначальное, а через 100 сек. - 100.
Как сказали выше, для того чтоб компилятор не оптимизировал и хранил в регистре а сохранял значение в ОЗУ используется ключевое слово volatile.
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.271 секунд. Запросов: 23.