Атомарное целочисленное приращение

Давайте иметь функцию, которая увеличивает глобальное атомарное целое число, а затем мы сохраняем это значение в неатомарной локальной переменной.

_Atomic int atom = 0 ;

void function( void )
{   
    int a = atom++ ;
}

Будет ли a всегда иметь уникальное значение при использовании приведенного выше кода с несколькими потоками. Если да, как предположили некоторые комментаторы, то какой смысл в таких функциях, как atomic_fetch_add.


person this    schedule 11.12.2013    source источник
comment
Что заставляет вас думать, что доступ к atomic_global_integer на самом деле является атомарным? Если бы это было так, a не получило бы одинаковое значение в двух потоках.   -  person interjay    schedule 11.12.2013
comment
@interjay Это атомарно. Например, определяется как _Atomic int;. a определенно может получить такое же значение в первом случае.   -  person this    schedule 11.12.2013
comment
В этом случае либо ваша реализация ошибочна, либо вы меняете atomic_global_integer в другом месте.   -  person interjay    schedule 11.12.2013
comment
@interjay Да, в другой теме. Пожалуйста, прочитайте весь вопрос.   -  person this    schedule 11.12.2013
comment
Я прочитал весь вопрос... Я имел в виду, что вы меняете его в другом месте своего кода.   -  person interjay    schedule 11.12.2013
comment
@interjay Нет, только в этой функции.   -  person this    schedule 11.12.2013
comment
Я поддерживаю свой комментарий об ошибке в коде, который вы не показали, или в реализации. Разместите полный пример, если считаете иначе.   -  person interjay    schedule 11.12.2013
comment
@self.: точка зрения Interjay заключается в том, что вы меняете atomic_global_integer где-то еще, а не в этом одном выражении atomic_global_integer++. Вы говорите «только в этой функции», но это не исключает другой фрагмент кода в той же функции. Альтернативой является то, что в вашей реализации C есть ошибка, или atomic_global_integer было объявлено неправильно (не как atomic), или какая-то проблема с препроцессором изменяет код (например, некоторые #define изменяют atomic_global_integer на что-то другое), или что поведение, которое вы предлагаете, не было реально наблюдается.   -  person Eric Postpischil    schedule 11.12.2013
comment
@interjay Ну, это моя проблема. Значит, между присвоением значения a не может произойти еще одно приращение? Позвольте мне отредактировать для ясности.   -  person this    schedule 11.12.2013
comment
@EricPostpischil Это единственная строка с приращением к этой переменной в коде.   -  person this    schedule 11.12.2013
comment
Еще одно увеличение может произойти в любой момент, но значения a все равно будут уникальными.   -  person interjay    schedule 11.12.2013
comment
@self.: Покажите код, включая определение и инициализацию atomic_global_integer. Кроме того, как вы пришли к выводу, что разные потоки могут получить одно и то же значение?   -  person Eric Postpischil    schedule 11.12.2013


Ответы (2)


Я не знаком с ключевым словом "_Atomic". Это что-то новое в C++11? В любом случае вы должны использовать атомарные встроенные функции, встроенные в большинство платформ и архитектур ЦП. (аля «заблокировать добавление» в x86).

В GCC/Clang функция хотела бы вызвать __sync_add_and_fetch . В Windows это называется InterlockedIncrement< /а>.

А на некоторых архитектурах gcc, которые по-прежнему нацелены на i386, вам придется делать это вручную в сборке. (Хотя это не будет работать на реальном 80386, внутренние функции блокировки не были представлены до 80486, но я отвлекся...)

unsigned int xadd_4(volatile void* pVal, unsigned int inc)
{
    unsigned int result;
    unsigned int* pValInt = (unsigned int*)pVal;

    asm volatile( 
        "lock; xaddl %%eax, %2;"
        :"=a" (result) 
        : "a" (inc), "m" (*pValInt) 
        :"memory" );

    return (result);
}

int AtomicIncrement(int* pInt)
{
    COMPILE_TIME_ASSERT(sizeof(int)==4);   
    // InterlockedIncrement
    unsigned int result = xadd_4(pInt, 1) + 1;
    return (int)result;
}
person selbie    schedule 11.12.2013
comment
Спасибо, это была информация, которую я искал. (Иногда SO может быть довольно бесполезным в отношении фактической информации.) - person this; 11.12.2013
comment
Я предполагаю, что мьютекс также будет работать (и даже с неатомными)? - person this; 11.12.2013
comment
_Atomic — это новое ключевое слово в C11, которое почти ничем не поддерживается. - person this; 11.12.2013
comment
Мьютекс тоже работает. И безопаснее для программистов, менее опытных в работе с потоками и атомарностью (и, вероятно, безопаснее для опытных программистов). - person selbie; 11.12.2013

Вы можете использовать сборочную вставку с операциями CompareAndSwap (cas). Для intel/gcc работает следующий пример (с использованием cmpxchg):

static inline uint32_t u32cas(volatile uint32_t *ptr, uint32_t old_val, uint32_t new_val) {
   uint32_t prev_val;
   __asm__ __volatile__("lock; cmpxchgl %k1,%2"
                        : "=a"(prev_val)
                        : "q"(new_val), "m"(*(volatile uint32_t *)(ptr)), "0"(old_val)
                        : "memory");
   return prev_val;
}


uint32_t atom = 0;


void atomic_increment() {
  uint32_t x;
  do {
    x = atom;
  } while(u32cas(&atom, x, x + 1) != x);
}

Кроме того, во FreeBSD вы можете использовать

атомный человек

person olegarch    schedule 11.12.2013