Как обойти отсутствие спецификации параллелизма в std :: get <> () - PullRequest
12 голосов
/ 07 июня 2019

Следующий блок кода:

  1. Технически недействителен, поскольку std::get<>() не является потокобезопасным.Ссылка: Использует ли `std :: get ` для `std :: tuple` гарантированно поточно-ориентированный для различных значений` I`?

  2. Насколько я могу судить, это действительно безопасно для всех значений std::tuple<> в дикой природе прямо сейчас и в обозримом будущем.

    #include <tuple>
    #include <atomic>
    #include <thread>

    // Out of my control
    using data_t = std::tuple<int, int, int, int>;
    void foo(data_t); 
    //

    int main() {
        data_t landing;
        std::atomic<int> completed = 0;

        // Whichever thread pings last will be the one performing foo()
        auto ping = [&](){
            if(++completed == 4) {
                foo(landing);
            }
        };

        std::thread a([&](){ std::get<0>(landing) = 1; ping(); });
        std::thread b([&](){ std::get<1>(landing) = 2; ping(); });
        std::thread c([&](){ std::get<2>(landing) = 3; ping(); });
        std::thread d([&](){ std::get<3>(landing) = 4; ping(); });

        a.join();
        b.join();
        c.join();
        d.join();

        return 0;
    }

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

Мои текущие варианты:

  • Эффективное повторное внедрение std::tuple<> с переформатированной std::get<> документацией, что является пустой тратой времени ипустая трата кода.
  • Выдвиньте предложение для std::get<>(std::tuple), чтобы предоставить гарантии, аналогичные std::vector<>, и задокументируйте тот факт, что код действителен только для еще не выпущенной версии стандарта.
  • игнорируйте проблему и полагайтесь на тот факт, что реально это почти наверняка всегда будет работать.

Ни один из них не особенно хорош в краткосрочной перспективе ... Итак, мои вопросы:

  • Я пропустил что-то, что лишает законной силы пункт # 2?
  • Существует ли лучший обходной путь, который позволил бы технически обосновать реализацию, не поддерживая чрезмерное количество дополнительного кода.
  • Любые другие мнения по этому вопросу приветствуются.

Ответы [ 2 ]

11 голосов
/ 07 июня 2019

Выдвиньте предложение для std::get<>(std::tuple), чтобы предоставить гарантии, аналогичные std::vector<>, и задокументируйте тот факт, что код действителен только для еще не выпущенной версии стандарта.

Я думаю, что это путь, так как он обеспечивает ценность для всего сообщества C ++ и не должен быть бременем для разработчиков.Это также прекрасная возможность написать ваше первое предложение .

Я предлагаю сделать это, а пока предположим, что это будет работать, даже если это UB.Если ваше программное обеспечение является сверхкритическим (например, система управления полетом), и вы хотите быть на 100% уверены, что не полагаетесь на что-то, что может сломаться в будущем ... тогда реализуйте свой собственный tuple.

0 голосов
/ 07 июня 2019
template<class...Ts>
std::tuple< std::remove_reference_t<T>* >
to_pointers( std::tuple<Ts...>& );

template<class T0, class...Ts>
std::array<T0, 1+sizeof...(Ts)>
to_array( std::tuple<T0, Ts...> const& );

запишите их.

int main() {
    data_t landing;
    std::atomic<int> completed = 0;

    // Whichever thread pings last will be the one performing foo()
    auto ping = [&](){
        if(++completed == 4) {
            foo(landing);
        }
    };

    auto arr = to_array(to_pointers(landing));

    std::thread a([&](){ *arr[0] = 1; ping(); });
    std::thread b([&](){ *arr[1] = 2; ping(); });
    std::thread c([&](){ *arr[2] = 3; ping(); });
    std::thread d([&](){ *arr[3] = 4; ping(); });

    a.join();
    b.join();
    c.join();
    d.join();

    return 0;
}

мы получаем доступ к элементам кортежа через указатели на них, а не через std::get.Проблема в спецификации std::get;как только у вас есть независимые указатели на независимые объекты, которые гарантированно существуют в кортеже, вы свободны от состояния гонки.

Итак, мы конвертируем кортеж в массив указателей в одном потоке (который в основном свободен), затембезопасно используйте его в темах.

...