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

Как использовать rand_r и как его использовать в потоковом безопасном режиме?

Я пытаюсь научиться использовать rand_r, и после прочтения этого вопроса я все еще немного смущен, может кто-то, пожалуйста, взгляните и укажите, что я Не хватает? Насколько я понимаю, rand_r принимает указатель на некоторое значение (или часть памяти с некоторым начальным значением) и использует его для генерации новых чисел каждый раз, когда он вызывается. Каждый поток, который вызывает rand_r, должен снабжать его уникальным указателем (или частью памяти) для получения "фактических случайных" чисел между разными потоками. Вот почему это:

int globalSeed;

//thread 1
rand_r(&globalSeed);

//thread 2
rand_r(&globalSeed);

- неправильный способ его использования. Если у меня есть

int seed1,seed2;

//thread 1
rand_r(&seed1);

//thread 2
rand_r(&seed2);

это был бы правильный способ генерации "истинных случайных" чисел между потоками?


EDIT: дополнительные вопросы после чтения ответов на вышеуказанную часть:

  • если в потоке 1 мне нужен случайный число от 1 до n, следует ли мне делать (rand_r(&seed1) % (n-1)) + 1? Или есть другой общий способ сделать это?
  • Правильно ли или нормально, если память для семени динамически распределена?
4b9b3361

Ответ 1

Это правильно. То, что вы делаете в первом случае, обходит принцип безопасности потока 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() с этим.

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