Безопасен ли rand()
поток? Может быть, а может и нет:
Функция rand () не обязательно должна быть реентерабельной. Функция, которая не требует повторного входа, не обязана быть поточно-ориентированной. "
Одним тестом и хорошим упражнением в обучении было бы заменить вызов rand()
, скажем, фиксированным целым числом и посмотреть, что произойдет.
Я думаю о генераторах псевдослучайных чисел как о чёрном ящике, который принимает целое число в качестве входных данных и возвращает целое число в качестве выходных. Для любого заданного входа выход всегда одинаков, но в последовательности чисел нет последовательности, и последовательность равномерно распределена по диапазону возможных выходов. (Эта модель не совсем точна, но она подойдет.) Способ, которым вы используете этот черный ящик, состоит в том, чтобы выбрать начальный номер (начальное число), использовать выходное значение в вашем приложении и в качестве входных данных для следующего вызова генератор случайных чисел. Существует два распространенных подхода к разработке API:
- Две функции: одна для установки начального начального числа (например,
srand(seed)
) и одна для извлечения следующего значения из последовательности (например, rand()
). Состояние PRNG хранится внутри в виде глобальной переменной. Генерация нового случайного числа либо не будет поточно-ориентированной (трудно сказать, но выходной поток не будет воспроизводимым), либо будет медленной в многопоточном коде (в результате вы получите некоторую сериализацию вокруг значения состояния).
- Интерфейс, в котором состояние PRNG предоставляется прикладному программисту. Здесь у вас обычно есть три функции:
init_prng(seed)
, которая возвращает некоторое непрозрачное представление состояния PRNG, get_prng(state)
, которая возвращает случайное число и изменяет переменную состояния, и destroy_peng(state)
, которая просто очищает выделенную память и т. Д. , Все PRNG с этим типом API должны быть поточно-ориентированными и работать параллельно без блокировки (потому что вы отвечаете за управление (теперь локально для потока) переменной состояния.
Я обычно пишу на фортране и использую реализацию Ладда PRNG Mersenne Twister (эту ссылку стоит прочитать). Есть много подходящих PRNG в C, которые выставляют состояние под ваш контроль. PRNG выглядит хорошо, и использование этого (с вызовами инициализации и уничтожения внутри параллельной области и частных переменных состояния) должно дать вам приличное ускорение.
Наконец, часто бывает так, что PRNG могут работать лучше, если вы запрашиваете целую последовательность случайных чисел за один раз (например, компилятор может векторизовать внутренние компоненты PRNG). Из-за этого у библиотек часто есть что-то вроде get_prng_array(state)
функций, которые возвращают массив, полный случайных чисел, как будто вы помещаете get_prng
в цикл, заполняющий элементы массива - они просто делают это быстрее. Это будет вторая оптимизация (и потребуется дополнительный цикл for внутри параллельного цикла for. Очевидно, что вы не хотите исчерпывать пространство стека для каждого потока, делая это!