#c #c #multithreading #random
#c #c #многопоточность #Случайный
Вопрос:
У меня есть несколько потоков, которые все выполняют одну и ту же функцию. В каждом из них они генерируют разное случайное число несколько раз. Мы пытались сделать это, поместив srand(time(0))
в начало функции, но, похоже, все они получают одинаковое число.
Нужно ли вызывать srand(time(0))
только один раз для каждой программы, т.Е. в начале main
(например), в начале каждой функции, которая вызывается несколько раз, или что-то еще?
Комментарии:
1. Вероятно, вам лучше использовать новые генераторы случайных чисел, которые появятся в C 0x. Какой компилятор вы используете?
2. Какую ОС вы используете windows / Linux??
3. если все потоки используют один и тот же srand(), вы получите одинаковые случайные числа
4. Не вызывайте rand() из нескольких потоков. Используйте генераторы случайных чисел в C 0x. Они также доступны в Boost.
5. Я заметил, что rand () запускается в одной и той же последовательности каждый раз, когда запускается поток. В моем приложении я запускаю один поток в цикле, и rand () повторяет одну и ту же последовательность на каждой итерации. rand () определенно не подходит для многопоточности. Я исправил это, используя те более новые генераторы C , которые были предложены выше.
Ответ №1:
srand() заполняет генератор случайных чисел. Вам нужно будет вызвать srand(time(NULL))
только один раз во время запуска.
Тем не менее, в документации говорится:
Функция
rand()
не является реентерабельной или потокобезопасной, поскольку она использует скрытое состояние, которое изменяется при каждом вызове. Это может быть просто начальное значение, которое будет использоваться при следующем вызове, или это может быть что-то более сложное. Чтобы получить воспроизводимое поведение в многопоточном приложении, это состояние должно быть сделано явным. Функцияrand_r()
снабжается указателем наunsigned int
, который будет использоваться в качестве состояния. Это очень маленький объем состояния, поэтому эта функция будет слабым генератором псевдослучайных чисел. Вместо этого попробуйтеdrand48_r
(3).
Выделенная часть вышеизложенного, вероятно, является причиной того, что все ваши потоки получают одинаковый номер.
Комментарии:
1. Неясно, как эта цитата, очень специфичная для конкретной реализации, применима к исходному вопросу. В последнем не указано, какая реализация используется.
rand()
может быть легко потокобезопасным. Это зависит от конкретной реализации.2. @AnT: Но
rand()
не требуется быть потокобезопасным, поэтому переносимая многопоточная программа не может безопасно использовать его (за исключением защиты с помощью мьютекса или подобного). C17 7.22.2.1p3: »rand
Функция не требуется, чтобы избежать перегонки данных с другими вызовами функций генерации псевдослучайных последовательностей [таких как она сама]».
Ответ №2:
Поскольку вы используете C , а не C, вы можете избежать проблем с потоковой обработкой, часто связанных с srand / rand, используя c 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.
Отвечая на этот вопрос, я сослался на Википедию C 11 и Boost random.
Ответ №3:
Со rand
справочной страницы:
Функция rand() не является реентерабельной или потокобезопасной, поскольку она использует скрытое состояние, которое изменяется при каждом вызове.
Поэтому не используйте его с многопоточным кодом. Используйте rand_r
(или drand48_r
, если вы используете linux / glibc). Введите в каждый RNG другое значение (вы могли бы ввести первый RNG в главном потоке, чтобы создать случайные начальные значения для тех, что находятся в каждом потоке).
Комментарии:
1. Интересно, что
rand
похоже, что это потокобезопасная версияrand_r
в glibc, а не наоборот (если я правильно интерпретирую исходный код ).2. Это не так. Вы смотрите именно на внутреннее состояние, которое делает его не потокобезопасным.
Ответ №4:
Если вы запускаете все потоки одновременно, время, отправляемое в srand, вероятно, одинаково для каждого потока. Поскольку все они имеют одинаковое начальное значение, все они возвращают одну и ту же последовательность. Попробуйте использовать что-то другое, например, адрес памяти из локальной переменной.
Комментарии:
1. Использование адреса памяти локальной переменной не является хорошим источником энтропии.
2. Использование другого начального значения неадекватно. Поскольку
srand
иrand
не являются потокобезопасными, они могут совместно использовать данные о состоянии. В таком случае вызовsrand
в одном потоке установит его для всех потоков, и, кроме того, могут возникнуть ошибки при обработке данных.
Ответ №5:
C не был разработан для многопоточности, поэтому поведение srand () при многопоточности не определено и зависит от библиотеки времени выполнения C.
Многие библиотеки времени выполнения C в Unix / Linux используют одно статическое состояние, доступ к которому небезопасен из нескольких потоков, поэтому в этих средах выполнения C вы вообще не можете использовать srand () и rand () из нескольких потоков. Другие среды выполнения Unix C могут вести себя по-другому.
Среда выполнения Visual C использует внутреннее состояние для каждого потока, поэтому безопасно вызывать srand() для каждого потока. Но, как указал Нил, вы, скорее всего, будете заполнять все потоки одинаковым значением — поэтому вместо этого заполняйте (time thread-id).
Конечно, для удобства переносимости используйте случайные объекты, а не функцию rand, и тогда вы вообще не зависели бы от скрытого состояния. Вам все еще нужен один объект на поток, и заполнение каждого объекта (time thread-id) по-прежнему является хорошей идеей.
Комментарии:
1. Я собираюсь прокомментировать «windows» просто для того, чтобы люди с большей готовностью воспринимали этот ответ при использовании MS Visual C в контексте Windows.
Ответ №6:
Это хороший вопрос. Я не могу ответить на это напрямую, потому что я думаю, что есть более серьезные проблемы. Кажется, даже не ясно, что rand вообще потокобезопасен. Он поддерживает внутреннее состояние и, похоже, не совсем четко определено, относится ли это к процессу или к потоку, и если это относится к процессу, если он потокобезопасен.
Чтобы быть уверенным, я бы блокировал мьютекс вокруг каждого доступа.
Или предпочтительно использовать более определенную генерацию, такую как из boost