как преобразовать неатомарную операцию в атомарную

Я пытаюсь понять атомарные и неатомарные операции. Относительно операционной системы, а также относительно C. Согласно странице википедии здесь

Рассмотрим простой счетчик, который могут увеличиваться разными процессами.
Неатомарно

Наивная, неатомарная реализация:
считывает значение в ячейке памяти;
добавляет единицу к значению;
записывает новое значение обратно в ячейку памяти.

Теперь представьте, что два процесса работают, увеличивая одну общую ячейку памяти:
первый процесс считывает значение в ячейке памяти;
первый процесс добавляет единицу к значению;
но до того, как он сможет записать новое значение обратно в ячейку памяти, оно приостанавливается, и второму процессу разрешается запускаться:
второй процесс считывает значение в ячейке памяти, то же значение, что и первый процесс;
второй процесс добавляет единицу к значение;
второй процесс записывает новое значение в ячейку памяти.

Как можно превратить описанную выше операцию в атоическую операцию. Я понимаю атомарные операции так, что все, что выполняется без прерывания, является атомарным. Так например

int b=1000;
  b+=1000;

Должна быть атомарная операция в соответствии с моим пониманием, потому что обе инструкции выполняются без прерывания, однако я узнал от кого-то, что в C нет ничего, известного как атомарная операция, поэтому оба оператора не являются атомарными. Итак, я хочу понять, чем отличается атомарность, когда речь идет о языках программирования от операционных систем?


person Registered User    schedule 10.07.2011    source источник
comment
asm - это ответ здесь, но это путь к темной стороне   -  person Ulterior    schedule 10.07.2011
comment
@David Heffernan, если вы хотите знать и контролировать выполнение, вы должны использовать asm, в противном случае компилятор будет на милость.   -  person Ulterior    schedule 10.07.2011
comment
@user на самом деле большинство людей используют библиотеки   -  person David Heffernan    schedule 10.07.2011
comment
@ Дэвид Хеффернан идет -1 за это ясное понимание   -  person Ulterior    schedule 10.07.2011
comment
@user: Я полагаю, вы не собирались голосовать против этого вопроса, поэтому +1, чтобы противодействовать этому.   -  person Sander De Dycker    schedule 10.07.2011


Ответы (5)


int b = 1000;
b+=1000;

превращается в несколько операторов на уровне инструкций. По крайней мере, подготовка регистра или памяти, присвоение 1000, затем получение содержимого этого регистра / памяти, добавление 1000 к содержимому и повторное присвоение нового значения (2000) этому регистру. Без блокировки ОС может приостановить процесс / поток в любой момент этой операции. Кроме того, в многопроцессорных системах другой процессор может получить доступ к этой памяти (в данном случае не регистр), пока ваша операция выполняется.

Когда вы снимаете блокировку (как вы бы сделали этот атомарный), вы частично информируете ОС, что нельзя приостанавливать этот процесс / поток и что к этой памяти не должны обращаться другие процессы.

Теперь приведенный выше код, вероятно, будет оптимизирован компилятором для простого присвоения 2000 области памяти для b, но я игнорирую это для целей этого ответа.

person Jim Deville    schedule 10.07.2011

В C99 нет способа сделать переменные атомарными по отношению к другим потокам. C99 не имеет понятия о нескольких потоках выполнения. Таким образом, вам необходимо использовать специфичные для компилятора расширения и / или инструкции на уровне процессора для достижения атомарности.

Следующий стандарт C, известный в настоящее время как C1x, будет включать атомарные операции.

Даже тогда простая атомарность просто гарантирует, что операция является атомарной, она не гарантирует, когда эта операция станет видимой для других процессоров. Чтобы добиться гарантий видимости, в C99 вам нужно будет изучить модель памяти вашего процессора и, возможно, использовать особый вид инструкций процессора, известный как ограждения или барьеры памяти. Вам также необходимо сообщить об этом компилятору, используя некоторый специфичный для компилятора барьер компилятора. C1x определяет несколько порядков памяти, и когда вы используете атомарную операцию, вы можете решить, какой порядок памяти использовать.

Некоторые примеры:

/* NOT atomic */
b += 1000;

/* GCC-extension, only in newish GCCs 
 *   requirements on b's loads are CPU-specific
 */
__sync_add_and_fetch(&b, 1000);

/* GCC-extension + x86-assembly, 
 *   b should be aligned to its size (natural alignment), 
 *   or loads will not be atomic
 */
__asm__ __volatile__("lock add $1000, %0" : "+r"(b));


/* C1x */
#include <stdatomic.h>
atomic_int b = ATOMIC_INIT(1000);
int r = atomic_fetch_add(&b, 1000) + 1000;

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

person ninjalj    schedule 10.07.2011

b+=1000 компилируется во всех известных мне системах по нескольким инструкциям. Таким образом, это не атомарно.

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

На самом деле C не имеет концепции потоков, поэтому в C. нет ничего атомарного. Вам нужно полагаться на конкретные детали реализации вашего компилятора и инструментов.

person David Heffernan    schedule 10.07.2011
comment
поэтому вы хотите сказать, что инструкция сборки, которая будет использоваться для доступа к той конкретной области памяти, где хранится переменная, будет атомарной. - person Registered User; 10.07.2011
comment
так что любой из int b; или b=1000; атомарен? - person Registered User; 10.07.2011
comment
int b не генерирует инструкции, поэтому вопрос не имеет смысла. Ничто в C не является атомарным. - person David Heffernan; 10.07.2011
comment
@ Зарегистрированный пользователь: является ли b = 1000 атомарным, зависит от ЦП и, возможно, от выравнивания и размера b. Даже в этом случае у вас может не быть (или иметь очень слабые) гарантии видимости. - person ninjalj; 10.07.2011

Вышеупомянутые операторы не атомарны, потому что они становятся инструкцией перемещения для загрузки b в регистр (если это не так), затем добавляют к нему 1000 и сохраняют обратно в память. Многие наборы инструкций допускают атомарность за счет атомарного приращения. Самый простой вариант - x86 с блокировкой addl dest, src; некоторые другие наборы инструкций используют cmpxchg для достижения того же результата.

person Jesus Ramos    schedule 10.07.2011

Итак, я хочу понять, чем отличается атомарность, когда речь идет о языках программирования от операционных систем?

Меня немного смущает этот вопрос. Что ты конкретно имеешь ввиду? Концепция атомарности одинакова как в prog. языки и ОС.

Что касается атомарности и языка, вот, например, ссылка на атомарность в JAVA, которая может дать вам другую точку зрения: Какие операции в Java считаются атомарными?

person Marc Polizzi    schedule 10.07.2011