Это верно. В первом случае вы обходите потокобезопасный характер rand_r
. Со многими не поточно-ориентированными функциями постоянное состояние сохраняется между вызовами этой функции (например, случайное начальное число здесь).
В поточно-ориентированном варианте вы фактически предоставляете поток данных (seed1
и seed2
), чтобы гарантировать, что состояние не будет разделено между потоками.
Имейте в виду, что это не делает числа действительно случайными, а просто делает последовательности независимыми друг от друга. Если вы начнете их с одного и того же начального числа, вы, вероятно, получите одинаковую последовательность в обоих потоках.
В качестве примера, скажем, вы получаете случайную последовательность 2, 3, 5, 7, 11, 13, 17 с начальным начальным числом 0. С общим начальным числом чередующиеся вызовы rand_r
из двух разных потоков вызовет это:
thread 1 thread 2
<--- 2
3 --->
<--- 5
7 --->
<--- 11
13 --->
<--- 17
и это лучший случай - вы можете обнаружить, что общее состояние повреждено, поскольку его обновления могут быть не атомарными.
В состоянии без общего доступа (a
и b
представляют два разных источника случайных чисел):
thread 1 thread 2
<--- 2a
2b --->
<--- 3a
3b --->
<--- 5a
5b --->
::
Некоторые поточно-ориентированные вызовы требуют, чтобы вы указывали состояние потока, подобное этому, другие могут создавать данные, относящиеся к потоку, под обложками (используя идентификатор потока или аналогичную информацию), чтобы вам никогда не приходилось об этом беспокоиться, Вы можете использовать один и тот же исходный код в многопоточных и непоточных средах. Я сам предпочитаю последнее, просто потому, что оно облегчает мою жизнь.
Дополнительные материалы для редактируемого вопроса:
> If in thread 1, I need a random number between 1 to n, should I do '(rand_r(&seed1) % (n-1)) + 1', or there is other common way of doing this?
Предполагая, что вы хотите значение от 1
до n
включительно , используйте (rand_r(&seed1) % n) + 1
. Первый бит дает значение от 0
до n-1
включительно, затем вы добавляете 1, чтобы получить желаемый диапазон.
> Is it right or normal if the memory for the seed is dynamically allocated?
Семя должно быть стойким, пока вы его используете. Вы можете динамически размещать его в потоке, но вы также можете объявить его в функции верхнего уровня потока. В обоих этих случаях вам нужно каким-то образом передавать адрес на более низкие уровни (если ваш поток не является единственной функцией, которая маловероятна).
Вы можете либо передать его через вызовы функций, либо как-нибудь настроить глобальный массив, чтобы нижние уровни могли найти правильный начальный адрес.
В качестве альтернативы, поскольку в любом случае вам нужен глобальный массив, вы можете иметь глобальный массив начальных значений, а не адресов начальных значений, которые нижние уровни могут использовать для обнаружения своих начальных значений.
Вы, вероятно, (в обоих случаях использования глобального массива) имели бы структуру ключа, содержащую идентификатор потока в качестве ключа и начальное число для использования. Затем вам нужно будет написать свою собственную rand()
подпрограмму, которая найдет правильное начальное число и с этим вызовет rand_r()
.
Это , поэтому я предпочитаю библиотечные процедуры, которые делают это под крышками с данными, специфичными для потоков.