Есть ли атомарный инкремент с проверкой предусловий, чтобы атомарное значение было меньше указанного значения?

Есть ли в новой стандартной операции атомарного приращения С++ предварительные условия проверки перед увеличением значения, чтобы атомарное значение было меньше указанного значения?

Могу ли я сделать это проще и быстрее, чем следующий код?

int atomic_inc(std::atomic_int& val, int less_than) {
 int new_val;
 int old_val = val.load();
 do
 {
   if (old_val > less_than) return old_val;
   new_val = old_val + 1;
 } while (!val.compare_exchange_weak(old_val, new_val));

 return new_val;
}

Если кто-то не знает, как работает compare_exchange_weak: compare_exchange_weak читает val, сравнивает с old_val и, если они не равны, сохраняет val в old_val. Если он равен, сохраните new_val в val.


person Alex    schedule 08.12.2012    source источник
comment
Я так не думаю. Ваш текущий код выглядит нормально. Если бы вы начали принимать какие-то другие проверки, вам нужно было бы обосновать, почему вы не допускаете другие (например, increment_if_zero_mod_two и т. д.).   -  person Kerrek SB    schedule 08.12.2012
comment
@KerrekSB - этот код выглядит сломанным: если val изменить после того, как он будет прочитан в old_val, это будет бесконечный цикл.   -  person djechlin    schedule 08.12.2012
comment
@djechlin: ну, как и в случае с любой петлей CAS, вы можете голодать, это правда. Но я бы не стал считать это нарушением кодекса.   -  person Kerrek SB    schedule 08.12.2012
comment
@djechlin: Можно ли избавиться от петли? Чтобы сделать его без ожидания, а не без блокировки. lock-free всегда имеет шанс оказаться в бесконечном цикле, но прогресс хотя бы в одном потоке всегда гарантирован.   -  person Alex    schedule 08.12.2012
comment
Вы должны читать из val в цикле, а не перед ним.   -  person djechlin    schedule 08.12.2012
comment
@djechlin: Нет, compare_exchange_weak читает val, сравнивает с old_val и, если они не равны, сохраняет val в old_val. Если он равен, сохраните new_val в val.   -  person Alex    schedule 09.12.2012
comment
@Alex - да, ты прав, я этого не знал.   -  person djechlin    schedule 09.12.2012
comment
@Alex, ваше решение аккуратное, поскольку оно позволяет избежать целочисленного переполнения. Наивным подходом было бы что-то вроде auto old= atomicint++; if (old<less_than) { . . ..   -  person Johan Lundberg    schedule 24.04.2016


Ответы (3)


Нет, специальной поддержки увеличения значений меньше значения нет. Ваш код настолько эффективен, насколько это возможно. В С++ 11 нет варианта без ожидания

Существует неограниченное количество возможных шаблонов «увеличить, если X». Производители оборудования решили, что единственное, что им нужно поддерживать, это «увеличить, если не изменить».

Теоретически вы могли бы изобрести аппаратную платформу со специальным ассемблерным кодом для нее, но C++11 не нацелен на это напрямую.

person Cort Ammon    schedule 12.09.2013

Что-то, что я делал в прошлом, может сработать для вас в зависимости от того, для чего вы это используете.

Если вы можете предположить, что val не будет обрезаться очень часто — так что возможная оптимизация отказа от CAS не сэкономит вам много — вы можете просто вслепую увеличить значение и настроить его после прочтения:

int atomic_inc(std::atomic_int& val, int less_than) {
    return std::min(++val, less_than);
}

А затем время от времени обрезайте val обратно до less_than, если это необходимо, достаточно часто, чтобы вам не приходилось беспокоиться о переполнении int, и вы были золотыми.

person Cory Nelson    schedule 12.09.2013

Если вы используете потоки, вы можете использовать мьютекс для выполнения атомарного приращения. Вот как бы вы поступили в этом случае:

Объявите и инициализируйте мьютекс глобально:

pthread_mutex_t lock;
pthread_mutex_init(&lock, NULL)

В одной теме:

int atomic_inc(std::atomic_int& val, int less_than) {
    pthread_mutex_lock(&lock);
    int newVal = val.load();
    if (newVal < less_than)
        newVal++
    pthread_mutex_unlock(&lock);
    return new_val;
}

Вам придется блокировать и разблокировать мьютекс, прежде чем изменять val в любом другом потоке.

Для получения дополнительной информации о мьютексе: http://www.yolinux.com/TUTORIALS/LinuxTutorialPosixThreads.html#SYNCHRONIZATION

person Uzair Farooq    schedule 08.12.2012
comment
Но теперь вы вносите раздор, не так ли? Весь смысл атомарных типов в том, что вам больше не нужно просто бросать мьютекс на все подряд. - person Lightness Races in Orbit; 08.12.2012
comment
Да, извините, я отрицаю это в соответствии с приведенным выше комментарием, использование блокировок по существу ортогонально ответу на вопрос. - person djechlin; 08.12.2012
comment
Но он хочет атомарный приращение, и нет никакого способа реализовать что-то атомарное, кроме как с помощью мьютекса или семафора. - person Uzair Farooq; 08.12.2012
comment
@Uzair Farooq: атомарное приращение может быть без мьютекса. Без ожидания всегда лучше, чем с мьютексом. Но для многих ядер lock-free может быть хуже, чем mutex. - person Alex; 08.12.2012