2017-01-04 4 views
2

Предположим, что у меня есть структура и класс в C++:Multithreaded Atomic магазин/Загрузка нескольких значений в C++

struct Vec { 
    double x; 
    double y; 
    double z; 
} 

class VecTracker { 
    Vec latest_vec; 
    std::atomic<double> highest_x; 
    std::atomic<double> highest_y; 
    std::atomic<double> highest_z; 

    //updates highest_x, highest_y, highest_z atomically 
    void push_vec(const Vec& v); 
    double get_high_x() const; 
    double get_high_y() const; 
    double get_high_z() const; 
    //returns Vec consisting of snapshot of highest_x, highest_y, highest_z 
    Vec get_highs() const; 
} 

Я есть R читателя темы и один поток записи. Нить писателя обновит ноль или более членов highest_*. Если поток нитей вызывает get_highs() Мне нужно, чтобы все записи из текущего вызова функции потока записи push_vec() были видны нити читателя до. Читательский поток читает highest_x, highest_y и т. Д. Для создания вектора.

Теперь, я знаю, что если Vec достаточно мало, я мог бы просто использовать std::atomic<Vec>. Проблема в том, что если она слишком велика, нельзя использовать собственные инструкции процессора для этих хранилищ/нагрузок. Есть ли способ использовать std::atomic_thread_fence, чтобы гарантировать, что многократная атомарная запись фиксируется потоком записи до того, как поток читателя подберет их? То есть, гарантия того, что все записи, написанные потоком писателя, будут зафиксированы до того, как поток читателя увидит любой из них? Или std::atomic_thread_fence предоставляет только предопределяющие гарантии в потоке? В настоящее время использование .store(std::memory_order_release) для каждого участника не гарантирует, что все три магазина произойдут до чтения.

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

Я знаю, что я мог бы поставить highest_x, highest_y и highest_z в одной и структура выделить две его копии на куче, поменяв указатели атомарно после каждой записи. Это единственный способ сделать это?

ответ

4

Дьявол находится здесь: //updates highest_x, highest_y, highest_z atomically. Как вы гарантируете, что они действительно атомные? Поскольку 3 удвоения не вписываются в 16B (самая большая атомная операция, которую я знаю на платформе X86_64), единственным способом обеспечить это будет использование mutex.

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

Чтобы решить вашу проблему, вы должны либо пойти с mutex - они достаточно эффективны, когда вы не уверены - или, если у вас аллергия на мьютексы, решение для обмена указателями вы описали сами.

+0

Aha. Да, я предположил, что только атомные операции на самом деле могут гарантировать атомное поведение. Похоже, что обмен указателем - это путь. Невозможно использовать Mutex или spinlock здесь, так как будет очень тяжелый спор, и я хотел бы гарантировать прогресс. – alfalfasprout

+0

Я искал, но не смог найти ссылку на инструкцию CMPXCHG32B. Если такая инструкция действительно существует, в этом случае было бы достаточно, так как в большинстве систем в настоящее время 3 удваивают только 24B. Вы, возможно, путаете его с CMPXCHG16B? В этом случае ваш аргумент имеет гораздо больший смысл. –

+0

@ ErikNyström, 100% да! Спасибо, что заметили. – SergeyA

Смежные вопросы