Безопасен ли поток mersenne twister для cpp

#include <random>

int f() {

    std::random_device seeder;
    std::mt19937 engine(seeder());
    std::uniform_int_distribution<int> dist(1, 6);

    return dist(engine);

}

Могут ли несколько потоков безопасно вызывать эту функцию? Является ли функция потокобезопасной? Избыточно звонить std::random_device seeder; и std::mt19937 engine(seeder()); каждый раз?


person cateof    schedule 17.11.2016    source источник
comment
Почему тег C? Это не имеет ничего общего с C.   -  person UnholySheep    schedule 17.11.2016
comment
Я действительно не понимаю, почему получаю отрицательные голоса. Я спрашиваю, является ли это потокобезопасным и избыточно ли обновлять семя.   -  person cateof    schedule 17.11.2016
comment
Наверное, из-за похожих вопросов, на которые есть пространные и развернутые ответы?   -  person Michael Foukarakis    schedule 17.11.2016
comment
Вашего кода недостаточно, чтобы продемонстрировать, что вы имеете в виду под потокобезопасностью. Инициализация? Поколение? Несколько экземпляров?   -  person Hatted Rooster    schedule 17.11.2016
comment
Возможный дубликат Как мне сгенерировать однородные случайные числа, безопасные для потоков?   -  person Michael Foukarakis    schedule 17.11.2016
comment
Ссылка, которой вы поделились, не решает мою проблему. Я пробовал код.   -  person cateof    schedule 17.11.2016
comment
Вопрос обновлен.   -  person cateof    schedule 17.11.2016
comment
Все локальные переменные потокобезопасны (если вы действительно не пытаетесь выстрелить себе в ногу), потому что к ним нельзя получить доступ из другого потока.   -  person NathanOliver    schedule 17.11.2016


Ответы (2)


Ни один из типов C ++ std не использует глобальные данные небезопасным способом. К двум не связанным между собой экземплярам такого типа можно получить доступ в разных потоках.

По умолчанию к одному экземпляру типа нельзя получить доступ из двух потоков без синхронизации.

Вам созданы локальные переменные. Эти локальные переменные не связаны ни с каким другим экземпляром своего типа. Здесь нет проблем с безопасностью потоков.

Псевдослучайные значения наиболее эффективно создаются при наличии состояния и его повторном использовании. Вы этого не делаете, поэтому создание случайного числа от 1 до 6 будет относительно дорогостоящим.

std::random_device seeder;
std::mt19937 engine(seeder());
std::uniform_int_distribution<int> dist(1, 6);
return dist(engine);

Ваше использование std::mt19937 излишне. Вы уже создаете random_device, который можно передать непосредственно в dist, а затем создать из него engine, а затем использовать engine. Использование engine здесь бесполезно.

Обычно вы создаете engine (какого-то типа, например mt19937) один раз из seeder. Затем вы сохраняете engine и многократно передаете его в дистрибутивы.

При этом относительно дорогостоящая генерация «реальных случайных чисел» выполняется один раз для генерации длинной серии псевдослучайных чисел через механизм через распределение.

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

«Правильный» способ сделать это - иметь объект, который генерирует для вас случайные значения, и передавать его туда, где вам это нужно. Сохранение начального начального числа также позволит вам повторить выполнение набора задействованных случайных чисел.

Если вам не нравится идея явного обхода вашего случайного состояния, вы можете использовать thread_local (или static с mutex охранником).

thread_local std::mt19937 engine(std::random_device{}());
std::uniform_int_distribution<int> dist(1, 6);
return dist(engine);

Это создает по одному engine на поток, а engine инициализируется значением из вашего random_device.

person Yakk - Adam Nevraumont    schedule 17.11.2016

Могут ли несколько потоков безопасно вызывать эту функцию? Является ли функция потокобезопасной?

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

Конечно, вывод из нескольких потоков может чередоваться, поскольку cout не синхронизируется.

Нужно ли инициализировать движок при каждом вызове функции

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

или было бы лучше поместить первые две строки (сеялку и двигатель) в конструктор класса?

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

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

person eerorika    schedule 17.11.2016