используя блокировку и копирование в поточно-небезопасной функции - PullRequest
0 голосов
/ 15 января 2019

Я новичок в C и многопоточности, у меня есть вопрос об использовании блокировки и копирования на небезопасной функции потока. Мой учебник гласит: « Генератор псевдослучайных чисел - простой пример этого класса небезопасной функции '

unsigned int next = 1;
int rand(void)
{
   next = next*1103515245 + 12345;
   return (unsigned int)(next/65536) % 32768;
}

void srand(unsigned int seed)
{
   next = seed;
}

и в моем учебнике также написано ' подход блокировки и копирования не будет работать для rand(), который опирается на статическое состояние при вызовах '

Я не понимаю, почему мы не можем переписать rand() как:

int rand(void)
{
   P(&mutex);
   next = next*1103515245 + 12345;
   V(&mutex);
   return (unsigned int)(next/65536) % 32768;
}

, где

void P(sem_t *s); /* Wrapper function for sem_wait */
void V(sem_t *s); /* Wrapper function for sem_post */

чтобы другие потоки не влияли на следующую статическую переменную текущего потока, что делает его функцией безопасности потока?

1 Ответ

0 голосов
/ 15 января 2019

Проблема, я думаю, в том, что значение next не зависит от потока, поэтому значения, возвращаемые одному потоку, теперь зависят от последовательности вызовов rand() в других потоках. Каждый вызов безопасен, но потоки все еще мешают друг другу. Определенность PRNG (генератора псевдослучайных чисел) может быть важна, если вы хотите воспроизвести симуляцию позже. Таким образом, вы обнаружите, что наборы функций, такие как набор POSIX drand48(), предоставляют как поточно-безопасные, так и небезопасные варианты:

double drand48(void);
double erand48(unsigned short xsubi[3]);
long jrand48(unsigned short xsubi[3]);
void lcong48(unsigned short param[7]);
long lrand48(void);
long mrand48(void);
long nrand48(unsigned short xsubi[3]);
unsigned short *seed48(unsigned short seed16v[3]);
void srand48(long seedval);

Из них drand48(), lrand48(), mrand48() не являются поточно-ориентированными, поскольку используют общее сохраненное начальное значение, установленное с помощью srand48() или seed48().

Функции erand48(), jrand48() и nrand48() являются поточно-ориентированными, поскольку начальное значение (три 16-битных значения в списке аргументов) передается функции.

Другая функция, lcong48(), предназначена для того, чтобы все расшевелить. Он изменяет начальное число, а также коэффициенты, используемые при генерации случайных чисел, работая между потоками.

Чтобы вернуться к своему коду, вам нужно сделать вариант с повторным вводом rand(). Фактически, POSIX определяет (но отмечает как устаревший) реентерабельный rand() - rand_r().

int rand_r(unsigned *seed);

Вы передаете указатель на начальное число в функцию при каждом использовании. Таким образом, вы можете написать свой собственный вариант - rand_ts() (для «поточно-ориентированного»):

int rand_ts(unsigned *next)
{
   *next = *next * 1103515245 + 12345;
   return (unsigned int)(*next / 65536) % 32768;
}

Теперь вы можете передать адрес переменной, содержащей начальное значение, и он будет обновляться при каждом вызове. Пока вы передаете локальную переменную потока с достаточной продолжительностью (обычно это переменная в стеке функции, выполняющейся в потоке), каждый поток сможет вычислять независимые серии случайных чисел, причем последовательность будет детерминированной для каждого нить.

Обратите внимание, что этот дизайн устраняет необходимость в мьютексе.

Альтернативный генератор random() обладает более сложным интерфейсом и лучшими свойствами случайности, но очень сложно поддерживать отдельные последовательности случайных значений для разных потоков (не невозможно, но довольно медленно и относительно медленно). Действительно, на странице предлагается использовать erand48(), jrand48() и nrand48(), если вам нужны независимые последовательности в нескольких потоках (поскольку интерфейс намного проще).

Обоснование и информация об использовании приложения после основных описаний на страницах POSIX могут быть весьма информативными, если их внимательно прочитать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...