Форум программистов, компьютерный форум, киберфорум
C/C++: WinAPI
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.85/40: Рейтинг темы: голосов - 40, средняя оценка - 4.85
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524

Как правильно получать значение interlocked переменной?

21.02.2021, 21:42. Показов 8487. Ответов 35

Студворк — интернет-сервис помощи студентам
Всем привет!
Как правильно получать значение interlocked переменной ???

"interlocked value" - это по другому атомарное значение.
Начиная с с++ можно использовать std::atomic

Но если, допустим, у вас слишком старый компилятор, то можно воспользоваться interlocked-функциями из WINAPI

На офф. сайте есть пример использования.


А так разве вообще можно делать?
Код на офф. сайте содержит баг?

Переменная модифицируется и читается одновременно из разных потоков:
C++
1
2
_InterlockedIncrement(&data);
printf_s("Thread %d: %d\n", threadNum, data);
Запись происходит атомарно.
Но чтение то переменной ничем не защищенно!

Получается, что код на офф. сайте содержит UB.

А как правильно получать значение interlocked переменной?
Среди списка имеющихся функций я так и не нашла такую, которая просто возвращает значение переменной.

Стек-оверфлоу предлагает какие то костыли по принципу:
C++
1
LONG result = InterlockedExchangeAdd(&value, 0);
Но ведь это оверхед на ровном месте!
Я хочу просто получить значение атомарной переменной.
При этом я не хочу модифицировать саму эту переменную!

Может быть есть какая то InterlockedExchangeGet ?
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
21.02.2021, 21:42
Ответы с готовыми решениями:

Как записать и получать значение переменной в другом классе
Добрый день. Имеется форма авторизации (с подключением к удаленной БД mysql), после ввода верного логина и пароля, необходимо получить...

Как правильно добавить значение переменной к ссылке?
echo "<a class='store' href=?".$Operation.">"; Помогите её туда отправить люди добрые... Добавлено через 4 минуты echo "<a...

Как правильно выстроить это выражение , чтобы получить значение переменной x ?
<script> var x=7; document.write('<?echo 5;echo 6;echo '<script>document.write(Math.round(x))</script>';?>'); </script> Как...

35
 Аватар для zayats80888
6354 / 3525 / 1428
Регистрация: 07.02.2019
Сообщений: 9,008
23.02.2021, 02:02
Лучший ответ Сообщение было отмечено eva2326 как решение

Решение

Студворк — интернет-сервис помощи студентам
Цитата Сообщение от eva2326 Посмотреть сообщение
зависание
Добавте volatile для stop и test, иначе это будет соптимизировано, т.к.
Цитата Сообщение от eva2326 Посмотреть сообщение
release
1
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
23.02.2021, 03:25  [ТС]
Тест не-атомарности:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
namespace
{
    char memory[2 * 1024];
    volatile uint32_t* test = 0;
    volatile uint32_t stop = 0;
 
    void thread1() 
    {
        for (;;)
        {
            *test = 0;
            const uint32_t check = stop;
            if (check != 0)
                break;
        }
    }
 
    void thread2() 
    {
        for (;;)
        {
            *test = uint32_t(-1); // 0xFFFFFFFF
            const uint32_t check = stop;
            if (check != 0)
                break;
        }
    }
 
    size_t aligned_address()
    {
        // остаток от деления на 1024 начального адреса массива <memory>
        size_t offset = size_t(memory) % 1024;
 
        // вычисляем смещение в массиве до адреса кратного 1024
        if (offset != 0)
            offset = 1024 - offset;
 
        // если смещение получилось слишком маленьким, то берём следующее кратное 1024
        if (offset < 100)
            offset += 1024;
 
        return offset;
    }
 
} // namespace
 
TEST_COMPONENT(002)
{
    // not worked
    stop = 0;
    const size_t offset = ::aligned_address();
    test = reinterpret_cast<uint32_t*>(&memory[offset - 2]); 
 
    std::thread t1(thread1);
    std::thread t2(thread2);
 
    size_t i = 0;
    bool success = false;
    for (i = 0; i != 10'000'000; ++i)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(300));
        const uint32_t t = *test;
        success = t != 0 && t != uint32_t(-1);
        if (success)
            break;
    }
    stop = true;
    t1.join();
    t2.join();
 
    std::cout << "number of attempts: " << i << '\n';
    ASSERT_TRUE(success);
}
Добавлено через 4 минуты
Цитата Сообщение от Ygg Посмотреть сообщение
в релизе не атомарность не детектируется. Не знаю с чем это связано, может нужно больше ядер на тестовом компе, у меня только два ядра. А может какие-то оптимизации сказываются

Похоже, что потоки просто не успевали стартовать.
Я вставила паузу, и вроде бы стабильно заработало.

Добавлено через 1 час 17 минут
Цитата Сообщение от zayats80888 Посмотреть сообщение
Возмите тот же счетчик ссылок в shared_ptr. Инкремент и декремент там выполняются с разными барьерами(для эффективности, а не просто ++ --)
Вот возьмите и покажите. И заодно расскажите: что там, как, и почему.
Сейчас это выглядит как голословное утверждение.

Цитата Сообщение от zayats80888 Посмотреть сообщение
когда ему на ввод поступают независимые инструкции,
Как он определяет что они независимые?

Сколько здесь независимых групп?
C++
1
2
volatile int a = 2 + 3 * 6;
volatile int b = 3 - 1;
Согласно правилам языка с++, любые оптимизации/перестановки могут быть только в рамках одной точки следования.
Например выражения:
C++
1
2
a = 2 + 3 * 6;
b =  3 - 1;
Могут быть перетасованы:
C++
1
2
a = 3 * 6 + 2;
b =  - 1 + 3;
Однако, язык гарантирует, что к тому моменту, когда начнется вычисление b, a уже полностью вычислено, и все связанные с ним сайд эффекты уже завершены.

Вот такой перестановки быть не может:
C++
1
2
b =  - 1 + 3;
a = 3 * 6 + 2;
Так как она нарушает алгоритм, заданный программистом, и может просто сломать его работу.
В частности, аппаратура может закладываться на порядок вычисления a и b.

Цитата Сообщение от zayats80888 Посмотреть сообщение
для него результат этого кода упорядочен.
"Результат упорядочен" - это примерно как "результат фиолетовый".

Результат не может быть упорядочен или не упорядочен. Он просто есть.
Упорядоченным (или не упорядоченным) может быть сам алгоритм, который к этому результату привел.

Если процессор начнет переставлять инструкции как ему вздумается, тогда алгоритм будет коверкаться.
И тогда, вместо корректного ожидаемого результата вы получите UB.

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

Ограничены чем?

Цитата Сообщение от zayats80888 Посмотреть сообщение
Вы хотя бы стандарту доверьтесь, т.к. отношения межпоточного упорядочивания там ясно прописаны(и про магическое переупорядочивание там тоже есть )
Приведите цитату из стандарта, где он описывает "межпоточное упорядочивание".
В особенности интересует "магическое переупорядочивание".

Цитата Сообщение от zayats80888 Посмотреть сообщение
Этот термин не зависит от архитектуры и отражает суть,
Термин - понятие, которое в рамках одной предметной области имеет строго одно единственное определение.
Термины нужны, что бы избежать неоднозначностей при описании явлений данной предметной области.

Если "общая память" - это действительно термин, а не ваша выдумка, тогда он должен быть где-то описан, и иметь четкое определение.
Предоставьте ссылку на первоисточник.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Вы сами спросили про синхронизацию. Если ваш инкремент существует ради инкремента, то не забивайте голову.
Вот эти два предложения, они вообще имеют хоть какую то логическую связь?

Инкремент существует, потому что он нужен алгоритму для решения задачи.
Факт его существования вообще никак не связан с синхронизациями.
И не имеет ни малейшего отношения к моему вопросу.

Мой вопрос был: зачем нужна какая то синхронизация для атомарных операций?
Ведь они итак уже атомарны.
Я получила ответ на свой вопрос:
Цитата Сообщение от Ygg Посмотреть сообщение
Если речь про весь второй абзац из моей предыдущей ссылки, то там говорится лишь о том, что две последовательные операции чтение и запись не могут гарантировано выполняться как единое неделимое целое. Другой поток может вклиниться между ними.
Некоторые сложные многоступенчатые процедуры невозможно выполнить атомарно.
И тогда на помощь приходит Interlocked API.
Оно позволяет запускать сложные многоступенчатые процедуры как атомарные.
То есть, атомарными они на самом деле не являются.
Но Interlocked API умеет имитировать "атомарность" за счет использования хитроумной синхронизации.
Эта синхронизация, по всей видимости, использует хитроумные lock free, хотя это не гарантируется.
И вот, что бы люди понимали, что деда мороза не существует, разработчики честно предупреждают: "мы используем синхронизацию".

Цитата Сообщение от zayats80888 Посмотреть сообщение
Я вам уже писал, что основное предназначение атомарных операций - синхронизация не атомарных операций.
Это - бред. Сами ещё раз прочитайте что вы пишите.

Атомарные операции не нуждаются в синхронизации, потому что они атомарные!
И не имеют ни малейшего отношения к неатомарным операциям.

Цитата Сообщение от zayats80888 Посмотреть сообщение
напишите val.fetch_add(1, std::memoty_order_relaxed), т.к. барьер по-умолчанию - удар по производительности(хотя если вы не понимаете этого, то лучше оставить как есть).
Расскажите, зачем нужны разные барьеры для обычного инкремента.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Пользователи бывают разные.
И зачем разным пользователям знать про разные барьеры, если им всем нужно лишь одно - увеличить счетчик на единицу?
0
 Аватар для zayats80888
6354 / 3525 / 1428
Регистрация: 07.02.2019
Сообщений: 9,008
23.02.2021, 06:52
Цитата Сообщение от eva2326 Посмотреть сообщение
Вот возьмите и покажите. И заодно расскажите: что там, как, и почему.
Сейчас это выглядит как голословное утверждение.
Прочтите этот абзац, там в конце как раз замечание про shared_ptr. Если вкратце, почему для декремента нужен aсquire барьер :
Кликните здесь для просмотра всего текста

Представим условный shared_ptr, который инкрементирует и декрементирует с relaxed ordering
который хранит указатель на что-то

И есть у нас два потока, у каждого своя копия shared_ptr c объектом.
Счетчик ссылок равен двум. И вот в первом потоке вызывается деструктор shared_ptr.
Последовательность действий деструктора такая:
1) атомарный декремент счетчика
2) если возвращенное значение 1 (последняя ссылка), то
3) чтение значения своего указателя в регистр
4) вызов деструктора объекта и деаллокация памяти по этому значению и т.д.

Только вот атомарный декремент и проверка условия не быстрая операция,
а предсказатель ветвлений может выполнить пункт 3) ещё до завершения 1) и 2).
Это простое чтение в регистр, которое никак не зависит от первых двух.
И вот не успел завершится 1), присходит переключение контекста потока.
Второй поток в это время вызывает reset() у shared_ptr, и записывает новое значение указателя,
потом вызывается деструктор, успешно выполняется этап 1), счетчик вернул 2, ничего не делаем.
Далее просыпается первый поток, выполняет первую операцию, счетчик ссылок вернул один,
можем выполнять этап 4), значение в регистр уже загружено,
только это значение, которое уже обработал второй поток, а сам записал новое.
Получим вызов деструктора уже убитого объекта и деаллокацию по невалидному адресу.

А вот если у декремента был бы aсquire барьер, то это запретило бы переупорядочивать операцию чтения,
а этап 3 выполнялся бы только после завершения атомарной операции.

Инкремент синхронизировать не с чем (у нашего shared_ptr только один счетчик ссылок, без weak_ptr)

У меня под рукой только gnu c++ библиотека, выкладывать кучу библиотечного кода и объяснять(разбираться в нём самому) неохота, но вот две основные атомарные операции для счетчиков (и use и weak):
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
// декремент
__gnu_cxx::__exchange_and_add_dispatch(&_M_use_count, -1)
// инкремент
__gnu_cxx::__atomic_add_dispatch(&_M_use_count, 1)
 
 
// обе раскрываются в read-modify-write операцию с release-acquire упорядочиванием
__atomic_fetch_add(__mem, __val, __ATOMIC_ACQ_REL)

Цитата Сообщение от eva2326 Посмотреть сообщение
Как он определяет что они независимые?
Например, они с разными регистрами работают.
Вы это вопрос инженерам задайте, которые чипы проектируют.
Вот пример алгоритма. https://en.wikipedia.org/wiki/Tomasulo_algorithm

Цитата Сообщение от eva2326 Посмотреть сообщение
Однако, язык гарантирует, что к тому моменту, когда начнется вычисление b, a уже полностью вычислено, и все связанные с ним сайд эффекты уже завершены.
И как переупорядочивание этих двух оперций процессором скажется на высокоуровневых гарантиях языка?
Где там сайд эфекты? Даже если они там есть, то до тех пор, пока вы в своём коде явно не закладываетесь на них,
компилятор может оптимизировать этот код как угодно. В рамках одного потока, если у вас дальше есть операция c = a + b (или использует сайд эффекты вычисления a и b), то вот она не будет переупорядочена с этими двумя.

Цитата Сообщение от eva2326 Посмотреть сообщение
Если процессор начнет переставлять инструкции как ему вздумается, тогда алгоритм будет коверкаться.
И тогда, вместо корректного ожидаемого результата вы получите UB.
Что значит как ему вздумается? Компилятор сгенерирует оптимальный код, где позволит процессору что-то переставлять, а где-то выставит барьеры и зависимости, что бы не нарушить высокоуровневые гарантии языка.

Цитата Сообщение от eva2326 Посмотреть сообщение
Приведите цитату из стандарта, где он описывает "межпоточное упорядочивание".
http://eel.is/c++draft/intro.m... ro.races-9

Цитата Сообщение от eva2326 Посмотреть сообщение
Если "общая память" - это действительно термин, а не ваша выдумка, тогда он должен быть где-то описан, и иметь четкое определение.
Предоставьте ссылку на первоисточник.
Много где встречал, например https://en.wikipedia.org/wiki/Shared_memory
(см. In hardware)
И вот тут(см. главу 2, да ивообще почитайте, очень интересно)https://www.cl.cam.ac.uk/~pes2... /test7.pdf
Цитата Сообщение от eva2326 Посмотреть сообщение
Вот эти два предложения, они вообще имеют хоть какую то логическую связь?
Инкремент существует, потому что он нужен алгоритму для решения задачи.
Факт его существования вообще никак не связан с синхронизациями.
Если вы не понимаете, что такое синхронизация и для чего нужна, это ещё ничего не значит.
Вот в стандарте про синхронизацию http://eel.is/c++draft/intro.m... ro.races-3 И далее, где она используется для межпоточного упорядочивания.
Цитата Сообщение от eva2326 Посмотреть сообщение
Мой вопрос был: зачем нужна какая то синхронизация для атомарных операций?
Для того, что бы писать эффективный многопоточный код.
Цитата Сообщение от eva2326 Посмотреть сообщение
Это - бред. Сами ещё раз прочитайте что вы пишите.
А вы книжку какую-нибудь почитайте.
Цитата Сообщение от eva2326 Посмотреть сообщение
Расскажите, зачем нужны разные барьеры для обычного инкремента.
Для того, что бы писать эффективный многопоточный код.
Цитата Сообщение от eva2326 Посмотреть сообщение
И зачем разным пользователям знать про разные барьеры, если им всем нужно лишь одно - увеличить счетчик на единицу?
Для того, что бы писать эффективный многопоточный код.
1
2737 / 894 / 331
Регистрация: 10.02.2018
Сообщений: 2,137
23.02.2021, 10:37
Цитата Сообщение от eva2326 Посмотреть сообщение
Похоже, что потоки просто не успевали стартовать.
Я вставила паузу, и вроде бы стабильно заработало.
Мне пауза не помогает. Возможно, что это зависит от срабатывания условия для P6 о нахождении данных в линии кэша (Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line). Нужен какой-то специалист по подобной фигне, для меня очень уж всё это загадочно выглядит.

Можно ещё другой тест. Один поток по очереди пишет 0 и -1 в переменную, а в main читаем и проверяем атомарность. Аналогично предыдущему варианту у меня неатомарность проявляется только для debug версии.
1
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
23.02.2021, 16:40  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
замечание про shared_ptr.
Заинтриговало.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Например, они с разными регистрами работают.
Почитала немножко об этом.
Если вкратце: гарантии с++ исполняются.
Как я и писала выше: ни компилятор, ни процессор не смогут переупорядочить выражения, которые имеют побочные эффекты.

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

Цитата Сообщение от zayats80888 Посмотреть сообщение
И как переупорядочивание этих двух оперций процессором скажется на высокоуровневых гарантиях языка?
Где там сайд эфекты?
В моём примере a и b имеют квалификатор volatile
Чтение/запись волатильных переменных = побочный эффект.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Даже если они там есть, то до тех пор, пока вы в своём коде явно не закладываетесь на них,
компилятор может оптимизировать этот код как угодно.
Нет никаких "явно закладываюсь".
Я уже давала вам ссылку на офф. сайт, где черным по белому описаны правила вычислений выражений.
К моменту начала вычисления выражения b, все побочные эффекты выражения a уже должны быть завершены.
И компилятор со всеми своими оптимизациями не имеет права нарушать этот контракт.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Компилятор сгенерирует оптимальный код, где позволит процессору
Я поняла, компилятор проконтролирует, что бы правила с++ соблюдались.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Много где встречал, например https://en.wikipedia.org/wiki/Shared_memory
(см. In hardware)
И вот тут(см. главу 2, да ивообще почитайте, очень интересно)https://www.cl.cam.ac.uk/~pes2... /test7.pdf
Не нашла.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Если вы не понимаете, что такое синхронизация и для чего нужна, это ещё ничего не значит.
Вы сейчас что, понтуетесь? )

Вы мне напомнили один анекдот про программистов.
Подходит один дешёвый понт к программисту, и говорит ему:
- Если ты не понимаешь, что такое синхронизация и для чего нужна, это ещё ничего не значит.

А программист ему отвечает:
- А если я понимаю, что такое синхронизация и для чего нужна, что тогда это значит?
Цитата Сообщение от zayats80888 Посмотреть сообщение
Для того, что бы писать эффективный многопоточный код.
У вас ошибка в логике.

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

Атомарные операции не нуждаются ни в какой синхронизации, и прекрасно могут обойтись и без неё.

Дальнейшие ваши ответы я никак не комментирую, потому что это не ответы, а какая то бесполезная чушь.
0
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
23.02.2021, 16:59  [ТС]
Название: nodata.png
Просмотров: 96

Размер: 394 байт

Цитата Сообщение от Ygg Посмотреть сообщение
Мне пауза не помогает.
Вы волатильность не забыли добавить?
0
 Аватар для zayats80888
6354 / 3525 / 1428
Регистрация: 07.02.2019
Сообщений: 9,008
23.02.2021, 17:29
Цитата Сообщение от eva2326 Посмотреть сообщение
Но тогда получается, что вот здесь - баг компилятора
Или есть какая то оговорка насчет глобальных переменных?
Не совсем понял, о чем вы. Ну и формально, согласно стандарту, ваш код содержит UB.
Цитата Сообщение от eva2326 Посмотреть сообщение
В моём примере a и b имеют квалификатор volatile
Чтение/запись волатильных переменных = побочный эффект.
Не обратил внимания. Где-то читал, что прерупорядочивать чтение/запись volatile объектов компилятор не может. Но вот не volatile операции "вокруг" них и с ними может вполне. В стандарте не нашел, нашел только заметку, что две атомарные операции над одним объектом не могут быть переупорядочены. http://eel.is/c++draft/basic.exec#intro.races-19
Цитата Сообщение от eva2326 Посмотреть сообщение
Не нашла.
А что искали? Там на инглише.
Цитата Сообщение от eva2326 Посмотреть сообщение
Вы сейчас что, понтуетесь? )
Вы мне напомнили один анекдот про программистов.
Вовсе нет, просто я вам про одно, вы мне про другое. Да и вообще я не программист(я не шучу).
Цитата Сообщение от eva2326 Посмотреть сообщение
У вас ошибка в логике.
Из того факта, что грамотное применение синхронизации позволяет написать более эффективный код, никак не следует, что синхронизация нужна конкретно атомарным операциям.
Где я это писал? Я вам дважды писал
Цитата Сообщение от zayats80888 Посмотреть сообщение
основное предназначение атомарных операций - синхронизация не атомарных операций.
Где тут про "синхронизация нужна конкретно атомарным операциям"?
Цитата Сообщение от eva2326 Посмотреть сообщение
Атомарные операции не нуждаются ни в какой синхронизации, и прекрасно могут обойтись и без неё.
О чем вы вообще говорите? Я вам давал ссылку на стандарт, где написано, что есть синхронизация. Атомарные операции сами являются синхронизирующими(не все).
0
2737 / 894 / 331
Регистрация: 10.02.2018
Сообщений: 2,137
23.02.2021, 17:54
Цитата Сообщение от eva2326 Посмотреть сообщение
Вы волатильность не забыли добавить?
Не, волатильность сразу пришлось добавить, так как без неё в релизе всё оптимизировалось до полной ерунды. Не знаю в чём дело, в ассемблере всё выглядит вполне годно и почти идентично debug, но атомарность работает иначе.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <thread>
#include <chrono>
 
#define ALIGN (1*1024)
 
char memory[2 * ALIGN];
volatile uint32_t* test = 0;
 
void thread1()
{
    for (;;)
        *test = 0;
}
 
void thread2()
{
    for (;;)
        *test = uint32_t(-1);
}
 
int main()
{
    // остаток от деления на ALIGN начального адреса массива <memory>
    size_t offset = size_t(memory) % ALIGN;
    // вычисляем смещение в массиве до адреса кратного ALIGN
    if (offset != 0)
        offset = ALIGN - offset;
    // если смещение получилось слишком маленьким, то берём следующее кратное ALIGN
    if (offset < 100)
        offset += ALIGN;
 
    //test = (uint32_t*) &memory[offset]; // атомарность выполняется
    test = (uint32_t*) &memory[offset - 2]; // атомарность не выполняется
 
    std::thread t1(thread1);
    std::thread t2(thread2);
 
    for (;;)
    {
        //std::this_thread::sleep_for(std::chrono::milliseconds(300));
        // проверка атомарности записи/чтения
        uint32_t t = *test;
        if ((t != 0) && (t != uint32_t(-1)))
            std::cout << std::hex << t << std::endl;
    }
 
    return 0;
}
ассемблерный код
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
void thread1()
{
   for (;;)
        *test = 0;
}
 
debug:
007E3C28  mov         eax,dword ptr [test (07F1AD8h)]  
007E3C2D  mov         dword ptr [eax],0  
007E3C33  jmp         thread1+28h (07E3C28h) 
 
release:
00021030  mov         eax,dword ptr [test (0253F8h)]  
00021035  mov         dword ptr [eax],0  
0002103B  jmp         thread1+5h (021035h)
 
 
 
void thread2()
{
   for (;;)
        *test = uint32_t(-1);
}
 
debug:
007E3DB8  mov         eax,dword ptr [test (07F1AD8h)]  
007E3DBD  mov         dword ptr [eax],0FFFFFFFFh  
007E3DC3  jmp         thread2+28h (07E3DB8h) 
 
release:
00021040  mov         eax,dword ptr [test (0253F8h)]  
00021045  mov         dword ptr [eax],0FFFFFFFFh  
0002104B  jmp         thread2+5h (021045h) 
 
 
 
int main()
{
    for (;;)
    {
        uint32_t t = *test;
        if ((t != 0) && (t != uint32_t(-1)))
            std::cout << std::hex << t << std::endl;
    }
}
 
debug:
007E57EE  mov         eax,dword ptr [test (07F1AD8h)]  
007E57F3  mov         ecx,dword ptr [eax]  
007E57F5  mov         dword ptr [ebp-40h],ecx  
007E57F8  cmp         dword ptr [ebp-40h],0  
007E57FC  je          ...
007E57FE  cmp         dword ptr [ebp-40h],0FFFFFFFFh  
007E5802  je          ....
 
release:
00021120  mov         eax,dword ptr [test (0253F8h)]  
00021125  mov         eax,dword ptr [eax]  
00021127  test        eax,eax  
00021129  je          main+0D0h (021120h)  
0002112B  cmp         eax,0FFFFFFFFh  
0002112E  je          main+0D0h (021120h)
1
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
10.03.2021, 19:05  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
Ну и формально, согласно стандарту, ваш код содержит UB.
С чего вы взяли, что там UB ???

Цитата Сообщение от zayats80888 Посмотреть сообщение
Где я это писал? Я вам дважды писал
Сообщение от zayats80888
основное предназначение атомарных операций - синхронизация не атомарных операций.
Предназначение атомарных операций - просто быть атомарными, и фсёёё.

C++
1
2
3
4
5
long v;
void foo()
{
    v = 10; // здесь ничего ни с чем не синхронизируется
}
Цитата Сообщение от zayats80888 Посмотреть сообщение
О чем вы вообще говорите? Я вам давал ссылку на стандарт, где написано, что есть синхронизация. Атомарные операции сами являются синхронизирующими(не все).
По ссылке, которую вы дали, нигде не ставится знак равенства между "синхронизацией" и "атомарной операцией".

Такое впечатление, что вы спутали название "Библиотеки атомарных операций" с собственно "атомарными операциями".

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

Сам std::atomic вообще не является примитивом синхронизации.
C++
1
2
3
4
5
std::atomic<int> v;
void foo()
{
    v = 10; // атомик ничего ни с чем не синхронизирует
}
На базе std::atomic можно изготовить спинлок.
Но это не делает сам std::atomic примитивом синхронизации.
И не является его основным предназначением.
И не имеет никакого отношения к собственно "атомарным операциям".

Добавлено через 3 минуты
Цитата Сообщение от zayats80888 Посмотреть сообщение
Где тут про "синхронизация нужна конкретно атомарным операциям"?
Мой изначальный вопрос был:

Цитата Сообщение от eva2326 Посмотреть сообщение
Мой вопрос был: зачем нужна какая то синхронизация для атомарных операций?
Если мне понадобится синхронизировать неатомарный код, я использую критическую секцию.

Вы мне пишите, якобы атомики нужны что бы синхронизировать неатомарный код.
Бред жеж.

Добавлено через 1 минуту
Цитата Сообщение от zayats80888 Посмотреть сообщение
А что искали?
Цитата Сообщение от eva2326 Посмотреть сообщение
Если "общая память" - это действительно термин, а не ваша выдумка, тогда он должен быть где-то описан, и иметь четкое определение.
...
0
 Аватар для zayats80888
6354 / 3525 / 1428
Регистрация: 07.02.2019
Сообщений: 9,008
10.03.2021, 19:47
Цитата Сообщение от eva2326 Посмотреть сообщение
С чего вы взяли, что там UB ???
Ваш код из поста 20(хотя вы его подредактировали после моего поста 21, там всё ещё UB)
1) где начинается lifetime объекта, на который указывает test?
2) согласно стандарту объект, на который указывает test - не атомарный и доступ к нему из разных потоков не синхронизирован, поэтому UB
Кликните здесь для просмотра всего текста
в частности, вод код - сгенерированный gcc 10.2
Assembler
1
2
3
4
5
6
7
8
(anonymous namespace)::thread1():
        mov     rdx, QWORD PTR (anonymous namespace)::test[rip]
.L4:
        mov     eax, DWORD PTR (anonymous namespace)::stop[rip]
        test    eax, eax
        je      .L4
        mov     DWORD PTR [rdx], 0 // ваше *test = 0 вынесено после цикла
        ret
Т.е. сам компилятор переупорядочил операции, имея на это полное право. Вы конечно можете возразить, сославшись на конкретный пункт стандарта, но тогда и я тоже сошлюсь, что бы возразить вам.


Цитата Сообщение от eva2326 Посмотреть сообщение
Предназначение атомарных операций - просто быть атомарными, и фсёёё.
Вы имеете полное право так думать(и так использовать атомарные операции). Но если ваш многопоточный код принимает решения на основании значений, прочитанных/записанных атомарно, то он явно или неявно полагается на синхронизацию, предоставляемую этими атомарными операциями. Иначе он содержит UB. (Не зря разработчики стандартной библиотеки сделали для атомарных операций самый жёсткий барьер по-умолчанию )

Цитата Сообщение от eva2326 Посмотреть сообщение
По ссылке, которую вы дали, нигде не ставится знак равенства между "синхронизацией" и "атомарной операцией".
Такое впечатление, что вы спутали название "Библиотеки атомарных операций" с собственно "атомарными операциями".
Из того факта, что "Библиотека атомарных операций" предоставляет доступ к барьерам памяти, никак не следует, что атомарные операции являются синхронизирующими.
Такое очучение, что вы читаете и видите то, что хотите видеть. Я не знаю, как вам это донести.
eva2326, вы вроде взрослый и грамотный человек и программист. Я, например, не знаю и не понимаю многих вещей, но когда мне начинают объяснять я не скалю зубы (типа, "я не понимаю, поэтому вы несёте какую-то чушь"), а стараюсь самостоятельно разобраться. Т.к. не знать или не понимать - это нормально.
Возьмите книгу Уильямса, например. Там целая глава посвящена атомарным операциям, постарайтесь разобраться, что они и для чего(вы то уж точно сможете).

Цитата Сообщение от eva2326 Посмотреть сообщение
Сам std::atomic вообще не является примитивом синхронизации.
Не удивительно, он же не является атомарной операцией
0
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
10.03.2021, 20:35  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
согласно стандарту объект, на который указывает test - не атомарный
С чего вы это взяли?
Насколько мне известно, стандарт такие вещи не регламентирует.
Нужно смотреть на гарантии, которые предоставляют конкретные платформы: раз и два

Цитата Сообщение от zayats80888 Посмотреть сообщение
вод код - сгенерированный gcc 10.2
В этой теме только cl (компилятор Visual Studio).

Цитата Сообщение от zayats80888 Посмотреть сообщение
Но если ваш многопоточный код принимает решения на основании значений, прочитанных/записанных атомарно, то он явно или неявно полагается на синхронизацию, предоставляемую этими атомарными операциями.
Да не предоставляют они никакой синхронизации.
Они вообще не используют никакой синхронизации.

Вы "атомарное чтение" с "барьером памяти" не перепутали?

Цитата Сообщение от zayats80888 Посмотреть сообщение
Такое очучение, что вы читаете и видите то, что хотите видеть.
Я вижу, что вы пытаетесь мне донести абсолютно бредовую мысль, что якобы атомарные операции используют синхронизацию.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Я не знаю, как вам это донести.
Мне не нужно.
Я уже получила все ответы на все вопросы данной темы.

Если вкратце: чтение/запись выровненных волатильных интов действительно атомарно.
0
 Аватар для zayats80888
6354 / 3525 / 1428
Регистрация: 07.02.2019
Сообщений: 9,008
10.03.2021, 21:01
Цитата Сообщение от eva2326 Посмотреть сообщение
С чего вы это взяли?
Насколько мне известно, стандарт такие вещи не регламентирует.
Нужно смотреть на гарантии, которые предоставляют конкретные платформы: раз и два
Хорошо, пусть ваш объект атомарный, но операции записи/чтения этого объекта в вашем коде не являются атомарными, согласно стандарту.
Атомарные операции регламентированы.
И что вы тогда подразумевали под багом компилятора?

Цитата Сообщение от eva2326 Посмотреть сообщение
В этой теме только cl (компилятор Visual Studio).
У меня нет под рукой cl, но это никак не отменяет UB в вашем коде. Судя по всему, в вашем случае он сделал что-то подобное.

Цитата Сообщение от eva2326 Посмотреть сообщение
Да не предоставляют они никакой синхронизации.
Они вообще не используют никакой синхронизации.
Т.е. ни стандарт, ни MSDN вам не авторитет. Все несут какую-то пургу про синхронизацию, которая никому не нужна. Я вас понял.

Цитата Сообщение от eva2326 Посмотреть сообщение
Я вижу, что вы пытаетесь мне донести абсолютно бредовую мысль, что якобы атомарные операции используют синхронизацию.
Да сколько можно то? Не используют, а предоставляют(или не предоставляют)! Использовать её должен ваш код!

Цитата Сообщение от eva2326 Посмотреть сообщение
Мне не нужно.
Я уже получила все ответы на все вопросы данной темы.
Я понял, ваш пост №8 был утверждением, а не вопросом.
0
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
10.03.2021, 21:49  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
Хорошо, пусть ваш объект атомарный, но операции записи/чтения этого объекта в вашем коде не являются атомарными, согласно стандарту.
Нет никаких "но".
Вы никогда не думали, почему атомарные объекты называют атомарными?

Цитата Сообщение от zayats80888 Посмотреть сообщение
И что вы тогда подразумевали под багом компилятора?
Нарушение program order
Компилятор оптимизировал чтение глобальной переменной.
Но разве компилятор имеет право делать такую оптимизацию?
Глобальные переменные, так же, как и волатильные, могут иметь побочные эффекты, источник которых может быть невидим для компилятора.
И тем не менее, компилятор должен учитывать, что переменная может неожиданно изменить своё значение.

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

А для обычных глобальных, он почему то думает, что на дворе 98, и треды в с++ ещё не завезли.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Т.е. ни стандарт, ни MSDN вам не авторитет. Все несут какую-то пургу про синхронизацию, которая никому не нужна. Я вас понял.
Нет, пургу несете конкретно вы, когда заявляете, что якобы атомарные операции предоставляют какую то синхронизацию.
Синхронизацию предоставляют всякие мутексы/барьеры, которые вообще ортогональны атомикам.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Да сколько можно то? Не используют, а предоставляют(или не предоставляют)! Использовать её должен ваш код!
Следующий код иллюстрирует атомарное чтение:
C++
1
std::cout << v;
Какую именно синхронизацию предоставляет это чтение.
И как именно её может использовать мой код?
Проиллюстрируйте на практике.

Сейчас внезапно выяснится, что это просто атомарное чтение.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Я понял, ваш пост №8 был утверждением, а не вопросом.
Как это интересно вы так умыдрились понять... ))
В #8 я задала вопрос, на который получила исчерпывающий ответ
0
 Аватар для zayats80888
6354 / 3525 / 1428
Регистрация: 07.02.2019
Сообщений: 9,008
10.03.2021, 22:40
Цитата Сообщение от eva2326 Посмотреть сообщение
Нет никаких "но".
Вы никогда не думали, почему атомарные объекты называют атомарными?
А вы никогда не думали, что никаких реальных операций над высокоуровневой абстракцией объекта может не быть? Покажите мне пункт в стандарте, на основании которого ваши операции над объектом в коде считаются atomic.
Цитата Сообщение от eva2326 Посмотреть сообщение
Нарушение program order
Компилятор оптимизировал чтение глобальной переменной.
Но разве компилятор имеет право делать такую оптимизацию?
program order описывает поведение высокоурвневой абстрактной машины.
(обратите внимание на примечания в конце).

Правила и поведение этой машины в многопоточном контексте описаны тут.
Ваш код эти правила не соблюдает.

В рамках однопоточного кода (на поведение которого вы и опираетесь со своими "точками следования", которых в с++11 давно нет) эти оптимизации не нарушают program order. Инач говоря, наблюдаемое поведение вашего кода не нарушается.

Цитата Сообщение от eva2326 Посмотреть сообщение
Нет, пургу несете конкретно вы, когда заявляете, что якобы атомарные операции предоставляют какую то синхронизацию.
Это не я заявляю, это в стандарте написано. И на MSDN.

Цитата Сообщение от eva2326 Посмотреть сообщение
Следующий код иллюстрирует атомарное чтение:
Это "код" ничего не иллюстрирует. Покажите реальный многпоточный код с атомарными операциями и я вам покажу, какие синхронизации они предоставляют или не предоставляют.
0
 Аватар для eva2326
1685 / 513 / 107
Регистрация: 17.05.2015
Сообщений: 1,524
11.03.2021, 00:10  [ТС]
Цитата Сообщение от zayats80888 Посмотреть сообщение
Покажите мне пункт в стандарте, на основании которого ваши операции над объектом в коде считаются atomic.
Сообщение #31, первый абзац

Цитата Сообщение от zayats80888 Посмотреть сообщение
(обратите внимание на примечания в конце).
Я не поняла, какое именно примечание имеется ввиду.

Цитата Сообщение от zayats80888 Посмотреть сообщение
В рамках однопоточного кода (на поведение которого вы и опираетесь со своими "точками следования"
С чего вы взяли, что я опираюсь на рамки однопоточного кода?
Просто любопытно: как вообще вы могли так подумать, учитывая, что исходный посыл диаметрально противоположный?

Цитата Сообщение от zayats80888 Посмотреть сообщение
Это не я заявляю, это в стандарте написано. И на MSDN.
Нет там такого. Не выдумывайте.

Там написано, что атомарные операции уже итак атомарные сами по себе, и никакая синхронизация им не нужна.
Однако, aтомарность гарантируется только для каких то простейших действий.
А если нужно что-то посложнее, тогда нужно использовать синхронизацию.

Цитата Сообщение от zayats80888 Посмотреть сообщение
Это "код" ничего не иллюстрирует. Покажите реальный многпоточный код с атомарными операциями
Нет, иллюстрирует.
Он показывает суть: просто берете, и читаете данные из переменной.
Не нужны никакие синхронизации. Никаких там барьеров или мутексов.
Вы просто осуществляете доступ к переменной без какой либо дополнительной защиты.

---



В том, что касается реального многопоточного кода...
Сейчас сложилось очень нехорошее впечатление о вас.
Вы вроде бы присутствуете в моей теме, но при этом совершенно не вникаете, и не понимаете о чем она.

Или как ещё можно объяснить тот факт, что реальный пример многопоточного кода я привела ещё в стартопике.
И именно вокруг этого примера крутится вся эта тема.
Этот пример кода уже тщательно рассмотрен и проанализирован.
Все необходимые нормативные документы уже прочитаны.
Ответы на вопросы уже получены.
Тесты написаны.
И саму эту тему, уже можно закрывать.

И тут вы, внезапно, просите показать реальный пример многопоточного кода.
0
11.03.2021, 00:52

Не по теме:

Цитата Сообщение от eva2326 Посмотреть сообщение
С чего вы взяли, что я опираюсь на рамки однопоточного кода?
Просто любопытно: как вообще вы могли так подумать, учитывая, что исходный посыл диаметрально противоположный?
Просто, что бы удовлетворить ваше любопытство:
Цитата Сообщение от eva2326 Посмотреть сообщение
Нет, не может.
Причем, это уже никак не зависит от "атомарности" операций.
Выражения write(A); и InterlockedExchange(X, 66); разделены точками следования.
С++ строго регламентирует и гарантирует порядок вычисления таких выражений.

Подробности см. в подразделах:
Rules (since C++11)
Sequence point rules (until C++11)
И кстати, там в комментарии речь идёт о простой атомарной операции записи X = 66, с InterlockedExchange(X, 66) переупорядовачивания как раз и не будет. И то, что вы в стартопике назвали оверхедом, как раз и используется для синхронизации не атомарных операций.

Цитата Сообщение от eva2326 Посмотреть сообщение
Сейчас сложилось очень нехорошее впечатление о вас.
Вы вроде бы присутствуете в моей теме, но при этом совершенно не вникаете, и не понимаете о чем она.
Вы спросили про синхронизацию, я лишь попытался объяснить.
Вместо того, что бы говорить, что я несу чушь, могли бы просто сказать, что вам это не нужно и не интересно и ничего вы синхронизировать не собираетесь в своём коде. Я бы прошел мимо. Постараюсь вас больше не беспокоить

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
11.03.2021, 00:52

Как получать доступ извне к переменной?
Добрый день друзья. Подскажите пожалуйста как получать доступ из вне к переменной, в данном случае answer,если она в квадратных скобках?...

Как при срабатывании SIGINT правильно передать значение переменной i в функцию closing
Имеется код: #include &lt;signal.h&gt; #include &lt;stdio.h&gt; #include &lt;stdlib.h&gt; #include &lt;unistd.h&gt; void closing(int number) {...

Как правильно получать данные в MVVM?
Каким образом правильно будет загружать данные из открываемого файла? 1. Вызывать командой через VM метод в модели, который сам...

Как правильно получать POST данные в обрабочтике ?
Нaпишите как правильно принимать POST данные в обработчике от регистрационной формы : $_POST У меня 5 частей формы : 1) Ник ...

[React] Как правильно получать components с сервера?
Есть проект. Он старенький. Расчёт идёт на то что бы перевести его с сервер-сайд рендринга, на фронт-сайд... Сейчас он работает на...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
36
Ответ Создать тему
Новые блоги и статьи
Копировать Windows на новый диск на UEFI
damix 17.10.2026
https:/ / uriesk. wordpress. com/ 2016/ 11/ 01/ move-window-10-to-ssd-on-a-uefi-dualboot-system-with-basic-linux-tools/ TL DR Вставляем диск, а старый подключаем через переходник, грузимся с лайва. . .
ИИ не может найти нужный язык в списке
Supersumestria 05.10.2026
Я ему даю вот такое изображение и прошу найти и подчеркнуть немецкий язык. Возвращает он вот это: https:/ / i. **********/ vqBWLe2. png Нужную строчку в 3й колонке просто выдумал. . Это. . .
Новая последняя моя музыка в SUNO
zorxor 05.10.2026
Здравствуйте, дорогие мои друзья! С большой радостью я хотел бы представить вам свою новую последнею музыку, которую сгенерировала мне по моей просьбе нейросеть SUNO. С уважением, zorxor. Это. . .
Программный домашний кинотеатр
russiannick 27.09.2026
Сподобился на программный домашний кинотеатр. В качестве ЯВУ по традиции выбрал js. В помощники взял Яндекс-Алису. Было создано три зала на разные интересы. исторические и ретро сериал Хичкок. . .
Беседа с ИИ о программистах, недопускающих к созданию и правке кода генеративные ИИ и причины этого
zorxor 21.09.2026
Раньше я радовался или получал некоторые эмоции, пусть небольшие, но всё же, от самого процесса написания кода, рекомпиляции и запуска, видя постепенное развитие программы и прочее. А теперь лень. . .
Мобильное приложение ColorStep
pavlinmavlin 17.09.2026
Реализовал приложение Красный, Зеленый, Синий в Unity3d + c#. Название изменил на ColorStep. Приложение прошло модерацию и теперь доступно для скачивания. Делал его сам, шаг за шагом — и вот,. . .
Запрет дублирования строк в табличной части
Maks 13.09.2026
Реализация из решения ниже выполнена на нетиповом справочнике "Нормы ТО" с табличной часть "Виды ТО", разработанного в КА2, со следующими реквизитами: - ВидТО (СправочникСсылка. ВидыТО); - ВидГСМ. . .
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр.
Jin X 06.09.2026
Скрипты Tampermonkey для CyberForum, ChatGPT, Claude и пр. Работая с форумом и нейросетями в браузере часто хочется что-то подкорректировать или добавить какого-то функционала. Ниже прикреплён. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru