Russian Qt Forum

Программирование => С/C++ => Тема начата: LisandreL от Март 30, 2012, 23:39



Название: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: LisandreL от Март 30, 2012, 23:39
В общем есть одна тян нитка, которая записывает значение int (только увеличивает) и несколько потоков, которые читают это значение.
Если было значение i1, а стало i2, то можно ли полагаться, что любая из читающих ниток получит i1 или i2, но никогда не получит, скажем, 2 байта от i1 и 2 байта от i2?
Или всё же придётся использовать мьютексы, локи или QAtomicInt?


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: m_ax от Март 30, 2012, 23:43
Я конечно не специалист во всех этих потока и т.д... Но наверное, QAtomicInt не просто так придумали)

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


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: V1KT0P от Март 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 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2427.html)


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: Igors от Март 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: тут конфуз с названием темы. На атомарность присваивания/чтения как раз полагаться можно, а вот на инкремент нельзя.


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: LisandreL от Март 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 );
делать?


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

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

Обычно отделаться модификацией "только в одной" не удается  :)


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: V1KT0P от Март 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 (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)


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: Igors от Март 31, 2012, 14:31
Если у процессора есть особые инструкции, то и инкремент/декремент/сложение/вычитание можно сделать атомарными.
А как Вы будете "делать"? Стоять со свечкой возле каждого + ? :) Вставлять его в ассемблере? Полагаться на "этот" процессор/платформу/компилятор?

Расходы на атомарность могут быть видимы только при интенсивной конкуренции на атомарной переменной, в остальных случаях их часто даже не удается замерить. Нарыть набор атомарных ф-ций (или сделать самому хоть на том же QAtomicInt) - всех делов 10 мин.


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: V1KT0P от Март 31, 2012, 14:36
Нарыть набор атомарных ф-ций (или сделать самому хоть на том же QAtomicInt) - всех делов 10 мин.
Да они уже давно есть в GCC : http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Atomic-Builtins.html (http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Atomic-Builtins.html).
Да и в MSVS я тоже где-то видел названия таких функций.


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: Igors от Март 31, 2012, 14:57
Да они уже давно есть в GCC : http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Atomic-Builtins.html (http://gcc.gnu.org/onlinedocs/gcc-4.6.3/gcc/Atomic-Builtins.html).
Там не наблюдается поддержки float/double. Неужели тогда придется "изобретать велосипед"?  :)


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: m_ax от Март 31, 2012, 15:12
Есть ещё std::atomic<T>


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: Akon от Март 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


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: Igors от Апрель 01, 2012, 07:51
Переменная должна быть volatile
Желательно, рекомендуется все такие переменные объявлять volatile. Хотя в данном случае разницы нет


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

В результате читающий поток увидит два значения - изначальное, а через 100 сек. - 100.


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

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


Название: Re: Можно ли полагаться на атомарность присваивания/чтения int?
Отправлено: Igors от Апрель 02, 2012, 10:39
Код:
        // что должно побудить компилятор на каждой итерации сохранять progress в памяти? 
Хотя бы то что переменная внешняя. И если someOperation приличная ф-ция то не будет он тратить драгоценный регистр на несчастный progress.

Все-таки как охотно мы обсуждаем легкие вопросы :) Предлагаю чуть усложниться, а то так неинтересно. Пусть у нас контейнер вместо переменной,
Код
C++ (Qt)
std::vector vec;
 
// 1-я нитка
if (!vec.size()) ...
 
// 2-я нитка
vec.push_back(..)
 
Здесь уже атомарно не выкрутиться, надо защищаться. Но мутекс неприемлемо дорог по сравнению с push_back (которое в большинстве случаев память не перераспределяет), а о size() неудобно и говорить.
Что бум делать?