Создание данных для чтения / записи atomi c в C11 G CC с использованием ? - PullRequest
0 голосов
/ 09 января 2020

Я узнал из SO потоков здесь и здесь , среди прочего, что небезопасно предполагать, что чтение / запись данных в многопоточных приложениях выполняется атомарно c в уровень ОС / аппаратного обеспечения, и повреждение данных может привести. Я хотел бы знать самый простой способ сделать чтение и запись int переменных atomi c, используя библиотеку <stdatomic.h> C11 с компилятором G CC на Linux.

Если я в настоящее время есть int назначение в потоке: messageBox[i] = 2, как мне сделать это назначение atomi c? В равной степени для теста чтения, как if (messageBox[i] == 2).

Ответы [ 3 ]

3 голосов
/ 09 января 2020

Для атома C11 вам даже не нужно использовать функции. Если ваша реализация (= компилятор) поддерживает атомики, вы можете просто добавить спецификатор atomi c к объявлению переменной, а затем все операции над этим атомом c:

_Atomic(int) toto = 65;
...
toto += 2;  // is an atomic read-modify-write operation
...
if (toto == 67) // is an atomic read of toto

Atomics имеют свою цену ( им нужно гораздо больше вычислительных ресурсов), но пока вы используете их едва ли, они являются идеальным инструментом для синхронизации потоков.

0 голосов
/ 11 января 2020

Если в настоящее время у меня есть назначение int в потоке: messageBox [i] = 2, как мне сделать это назначение atomi c? В равной степени для теста чтения, например if (messageBox [i] == 2).

Вам почти никогда не нужно ничего делать. Почти в каждом случае данные, которыми ваши потоки обмениваются (или обмениваются данными), защищены от одновременного доступа с помощью таких вещей, как мьютексы, семафоры и тому подобное. Реализация базовых операций обеспечивает синхронизацию памяти.

Причиной такого атомизма является помощь в построении более безопасных условий гонки в вашем коде. Есть ряд опасностей с ними; в том числе:

ai + = 7;

будет использовать протокол atomi c, если ai определено надлежащим образом. Попытка расшифровать условия гонки не помогает скрыть реализацию.

В них есть также сильно машинно-зависимая часть. Например, строка выше может давать сбой [1] на некоторых платформах, но как этот сбой сообщается программе? Это не [2].

Только одна операция имеет возможность справиться с ошибкой; atomic_compare_exchange_ (слабый | сильный). Слабый просто пытается один раз, и позволяет программе выбрать, как и следует ли повторить попытку. Сильные повторы бесконечно. Недостаточно просто попробовать один раз - возможны ложные сбои из-за прерываний, - но бесполезные повторные попытки при не поддельных сбоях также не годятся.

Возможно, для надежных программ или широко применимых библиотек, единственное, что вы должны использовать, это atomic_compare_exchange_weak ().

[1] Связанное с нагрузкой, условное хранилище (ll-s c) является распространенным средством для выполнения транзакций atomi c на асинхронной шине архитектуры. Связанный с нагрузкой устанавливает небольшой флажок в строке кэша, который будет очищен, если какой-либо другой агент шины попытается изменить эту строку кэша. Условное хранилище хранит значение, если в кэше установлен маленький флаг, и очищает флаг; если флаг сброшен, условное хранилище сообщает об ошибке, поэтому можно попытаться выполнить соответствующую операцию повтора. Из этих двух операций вы можете создать любую операцию atomi c, которая вам нравится на полностью асинхронной архитектуре шины.

ll-s c может иметь тонкие зависимости от атрибутов кэширования Местоположение. Допустимые атрибуты кэша зависят от платформы, как и то, какие операции могут выполняться между ll и s c.

Если вы поставили операцию ll-s c на плохо кэшированный доступ и слепо повторите попытку, ваша программа заблокируется. Это не просто предположение; Мне пришлось отлаживать один из них в «безопасной» системе на основе ARMv7.

[2]:

#include <stdatomic.h>
int f(atomic_int *x) {
    return (*x)++;
}
f:
        dmb     ish
.L2:
        ldrex   r3, [r0]
        adds    r2, r3, #1
        strex   r1, r2, [r0]
        cmp     r1, #0
        bne     .L2       /* note the retry loop */
        dmb     ish
        mov     r0, r3
        bx      lr
0 голосов
/ 10 января 2020

то, что небезопасно предполагать, что чтение / запись данных в многопоточных приложениях атомарно c на уровне операционной системы / аппаратного обеспечения, и повреждение данных может привести к

На самом деле несоставные операции над типами, такими как int, являются атомами c при любой разумной архитектуре. То, что вы читаете, - просто обман.

(Инкремент - это составная операция: она имеет компонент чтения, вычисления и записи. Каждый компонент имеет атоми c, но вся составная операция - не .)

Но атомарность на аппаратном уровне не является проблемой. Используемый вами язык высокого уровня просто не поддерживает такого рода манипуляции с обычными типами. Вы должны использовать типы Atomi c, чтобы даже иметь право манипулировать объектами таким образом, чтобы вопрос атомарности был актуален: когда вы потенциально модифицируете объект, используемый в другом потоке.

(Или изменчивые типы. Но не используйте изменчивые. Используйте атомные.)

...