Есть ли в C ++ "не равно сравнивать и обменивать" или "добавлять и добавлять не равно"? - PullRequest
0 голосов
/ 13 июня 2018

Или любой другой способ реализации?

Давайте представим атом:

std::atomic<int> val;
val = 0; 

Теперь я хочу обновить val, только если val не равен нулю.

if (val != 0) {
    // <- Caveat if val becomes 0 here by another thread.
    val.fetch_sub(1);  
}

Так может быть:

int not_expected = 0;
val.hypothetical_not_compare_exchange_strong(not_expected, val - 1);

На самом деле вышеприведенное также не будет работать, потому что val может обновляться между val - 1 и гипотетической функцией.

Может быть это:

int old_val = val;
if (old_val == 0) {
    // val is zero, don't update val. some other logic.
} else {

    int new_val = old_val - 1;
    bool could_update = val.compare_exchange_strong(old_val, new_val);
    if (!could_update) {
        // repeat the above steps again.
    } 
}

Редактировать:

val - это переменная счетчика, не связанная с уничтожением объекта.Это должно быть без знака (так как число никогда не может быть отрицательным).

Из потока A: если тип 2 отправлен, тип 1 не может быть отправлен, если счетчик типа 2 не равен 0.

while(true) {
    if counter_1 < max_type_1_limit && counter_2 == 0 && somelogic:
        send_request_type1();
        counter_1++;

    if some logic && counter_2 == 0:
        send_request_type2();
        counter_2++;
}

поток B & C: ответ ручки:

if counter_1 > 0:
     counter_1-- 
     // (provided that after this counter_1 doesn't reduce to negative)
else 
     counter_2--

1 Ответ

0 голосов
/ 13 июня 2018

Общий способ реализации недоступных атомарных операций - использование цикла CAS ;в вашем случае это выглядело бы так:

/// atomically decrements %val if it's not zero; returns true if it
/// decremented, false otherwise
bool decrement_if_nonzero(std::atomic_int &val) {
    int old_value = val.load();
    do {
        if(old_value == 0) return false;
    } while(!val.compare_exchange_weak(old_value, old_value-1));
    return true;
}

Итак, поток B & C будет:

if(!decrement_if_nonzero(counter_1)) {
    counter_2--
}

, а поток A может использовать простые атомные нагрузки / приращения - поток A являетсяединственный, кто увеличивает счетчики, поэтому его проверка о том, что counter_1 находится под определенным порогом, всегда будет выполняться независимо от того, что делают потоки B и C.

Единственная «странная» вещь, которую я вижу, это counter_2 логика исправления - в потоке B & C он уменьшается без проверки на ноль, в то время как в потоке A он увеличивается только на ноль - это выглядит как ошибка.Вы хотели также обнулить его до нуля в потоке B / C?


При том, что атомы велики и все, но сложнее получить права, так что если бы я реализовывал этот видлогика Я бы начал с мьютекса, а затем перешел бы к атомике, если профилирование показало, что мьютекс был узким местом.

...