Подтвердить что ты не робот

Использование stdlib rand() из нескольких потоков

У меня есть несколько потоков, которые выполняют одну и ту же функцию. В каждом из них несколько раз генерируются разные случайные числа. Мы попытались сделать это, положив srand(time(0)) в начале функции, но кажется, что все они получают одинаковое число.

Нужно ли вызывать srand(time(0)) только один раз для каждой программы, т.е. в начале main (например), в начале каждой функции, которая вызывается несколько раз или что-то еще?

4b9b3361

Ответ 1

srand() использует генератор случайных чисел. Вы должны только вызывать srand(time(NULL)) один раз во время запуска.

Тем не менее, в документации указано:

Функция rand() не реентерабельная или потокобезопасным, поскольку он использует скрытые состояние, которое изменяется при каждом вызове. Это может быть только начальное значение для будет использоваться следующим вызовом, или он может быть чем-то более сложным. В порядке получить воспроизводимое поведение в это приложение должно быть явным. Функция rand_r() поставляется с указателем на a unsigned int, который будет использоваться как состояние. Это очень небольшое количество состояний, поэтому эта функция будет слабой псевдослучайный генератор. Пытаться drand48_r (3).

Подчеркнутая часть вышеизложенного, вероятно, является причиной того, что все ваши потоки получают одинаковое число.

Ответ 2

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

Ответ 3

На странице rand:

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

Поэтому не используйте его с потоковым кодом. Используйте rand_r (или drand48_r, если вы используете linux/glibc). Поместите каждый RNG другим значением (вы можете засеять первый RNG в основном потоке, чтобы получить случайные семена для каждого в каждом потоке).

Ответ 4

Поскольку вы используете С++, а не C, вы можете избежать проблем с потоками, часто связанных с srand/rand, с помощью С++ 11. Это зависит от использования последнего компилятора, который поддерживает эти функции. Вы должны использовать отдельный движок и распределение по каждому потоку. Пример действует как кубик.

#include <random>
#include <functional>

std::uniform_int_distribution<int> dice_distribution(1, 6);
std::mt19937 random_number_engine; // pseudorandom number generator
auto dice_roller = std::bind(dice_distribution, random_number_engine);
int random_roll = dice_roller();  // Generate one of the integers 1,2,3,4,5,6.

Я ссылался на Wikipedia С++ 11 и Повысить случайность при ответе на этот вопрос.

Ответ 5

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

Чтобы быть уверенным, что я блокирую мьютекс вокруг каждого доступа.

Или желательно использовать более четкое генерирование, например, из boost

Ответ 6

C не был разработан для многопоточности, поэтому поведение srand() с многопоточным движением не определено и зависит от библиотеки времени выполнения C.

Многие библиотеки времени исполнения Unix/Linux C используют одно статическое состояние, которое небезопасно для доступа к нескольким потокам, поэтому при этих сеансах C вы не можете использовать srand() и rand() из нескольких потоков вообще. Другие временные ряды Unix C могут вести себя по-разному.

Среда исполнения Visual С++ использует внутреннее состояние для каждого потока, поэтому безопасно вызывать srand() для каждого потока. Но, как отметил Нейл, вы, вероятно, засечете все потоки с одинаковым значением, поэтому вместо этого используйте семя (time + thread-id).

Конечно, для переносимости используйте случайные объекты, а не функцию rand, и тогда вы вообще не будете зависеть от скрытого состояния. Вы по-прежнему нуждаетесь в одном объекте в потоке, и посев каждого объекта с помощью (time + thread-id) по-прежнему является хорошей идеей.

Ответ 7

Все они получают одинаковый номер, потому что вы предположительно запускаете все потоки одновременно, или все они используют одно и то же статическое семя, и в этом случае вы немного набиты. Вам нужен лучший источник энтропии, чем time(). Тем не менее, быстрый взлом будет состоять из (time * thread-id), где thread-id - это идентификатор каждого рабочего потока.

Конечно, правильное решение в С++ заключается не в использовании функций генератора случайных чисел, а в использовании объектов генератора случайных чисел, подобных тем, которые предоставляются библиотекой случайных чисел Boost, которые по своей природе (поскольку они основаны на стеках ) являются потокобезопасными. См. этот ответ, который я подготовил ранее для примера. Тем не менее, по-прежнему может возникнуть проблема, обеспечивающая достаточную энтропию в программе MT, поскольку использование функции времени() будет по-прежнему иметь проблему, о которой я упоминал выше.