Атомика и многопоточность в C

#c #multithreading #atomic

Вопрос:

Поэтому я возился с atomic и thread и создал эту программу, которая считывает и записывает данные в массив в зависимости от того, задействован ли atomic.

Однако при компиляции результаты, по-видимому, различаются.

РЕДАКТИРОВАТЬ: под различиями я подразумеваю, что «k» и «out» не идентичны при компиляции.

 #include <atomic>
#include <iostream>
#include <thread>
#include <vector>

std::atomic<bool> busy (false);
std::atomic_int out (0);

void do_thing(std::vector<int>* inputs) {
    while(!busy) {
        std::this_thread::yield();
    }

    int next = inputs->back();
    inputs->pop_back();
    
    out.store(out.load(std::memory_order_relaxed)   next, std::memory_order_relaxed);
}

int main(void) {

    std::vector<int> inputs;
    for(int i = 0; i < 100; i  ) inputs.push_back(rand() % 10);

    // base
    int k = 0;
    for(autoamp; el : inputs) k  = el;
    std::cout << k << std::endl;

    // threaded
    try {
        std::vector<std::thread> threads;
        for(int i = 0; i < 100; i  ) threads.push_back(std::thread(do_thing, amp;inputs));
        busy = true;
        for (autoamp; th : threads) th.join();
        std::cout << out.load(std::memory_order_relaxed);
        std::cout << std::endl;
    }
    catch (const std::exceptionamp; ex) {
        std::cout << ex.what() << std::endl;
    }

    return 0;
}
 

Может ли кто-нибудь указать, что происходит?

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

Овации,

K

Комментарии:

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

2. Что тут объяснять? У вас есть доступ к множеству рас данных inputs . Результаты неопределенного поведения.

3. Все ваши потоки изменяют вектор без синхронизации.

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

5. Просто потому out , что это «атомарная» переменная, это не делает out.store(out.load(...)   next,...); атомарную операцию. Два (или более) разных потока могут вызывать out.load(...) и получать одно и то же значение, затем каждый может добавить свое собственное next к этому значению и сохранить его обратно. Один из next s просто потерялся-перезаписан действием другого потока. Отдельные out.xxxxx() вызовы были атомарными, но последовательность -нет.