Является ли rand_r реальным потокобезопасным? - PullRequest
6 голосов
/ 05 мая 2010

Ну, функция rand_r должна быть поточно-безопасной. Однако, из-за его реализации, я не могу поверить, что он может заставить себя не изменяться другими потоками. Предположим, что два потока будут вызывать rand_r одновременно с одной и той же переменной seed. Таким образом, гонка чтения-записи произойдет. Код rand_r, реализованный glibc, приведен ниже. Кто-нибудь знает, почему rand_r называется потокобезопасным?

 int
    rand_r (unsigned int *seed)
    {
      unsigned int next = *seed;
      int result;

      next *= 1103515245;
      next += 12345;
      result = (unsigned int) (next / 65536) % 2048;

      next *= 1103515245;
      next += 12345;
      result <<= 10;
      result ^= (unsigned int) (next / 65536) % 1024;

      next *= 1103515245;
      next += 12345;
      result <<= 10;
      result ^= (unsigned int) (next / 65536) % 1024;

      *seed = next;

      return result;
    }

Ответы [ 3 ]

18 голосов
/ 05 мая 2010

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

1) Не безопасно для всех. Небезопасно вызывать функцию одновременно из нескольких потоков. Например, strtok.

2) Поток безопасен по отношению к системе. Безопасно вызывать функцию одновременно из нескольких потоков, при условии, что разные вызовы работают с разными данными. Например, rand_r, memcpy.

3) Потокобезопасен по отношению к данным. Безопасно вызывать функцию одновременно из нескольких потоков, даже работая с одними и теми же данными. Например pthread_mutex_lock.

rand_r находится на уровне 2, и в контексте C (в частности, в спецификации POSIX) принято называть этот потокобезопасным.

В некоторых других языках, таких как Java, принято называть уровень 3 «потокобезопасным», а все остальное - «не потокобезопасным». Так, например, java.util.Vector является "потокобезопасным", а java.util.ArrayList - "не безопасным для потоков". Конечно, все методы java.util.ArrayList находятся на уровне 2. Так что программист, пришедший из Java, может естественно назвать rand_r и memcpy «не поточно-ориентированными».

В Си соглашение другое, возможно, потому что внутренне синхронизированные структуры данных довольно редки с самого начала. В контексте C вы могли бы спросить «файловые дескрипторы поточно-ориентированы?» И говорить об уровне 3, но когда спрашиваете «эта функция поточно-ориентирована?» это обычно означает уровень 2.

13 голосов
/ 05 мая 2010

rand_r потокобезопасен, потому что функция полностью чиста. Он не читает и не изменяет любое состояние, кроме аргументов. Поэтому его можно безопасно вызывать одновременно.

Это отличается от большинства rand функций, которые хранят состояние (начальное число) в глобальной переменной.

Предположим, что два потока будут вызывать rand_r одновременно с одной и той же переменной seed.

Я предполагаю, что вы имеете в виду что-то вроде этого

int globalSeed;

//thread 1
rand_r(&globalSeed);

//thread 2
rand_r(&globalSeed);

Это не означает, что функция не является поточно-ориентированной, это означает лишь то, что вы используете ее без поточной защиты, предоставляя выходной параметр, который может быть доступен / изменен другим потоком.

Это то же самое, что записать результат функции в глобальную переменную, которая может быть доступна / изменена другим потоком. Это не означает, что функция не является поточно-ориентированной, это означает, что ваш код не является поточно-безопасным.

0 голосов
/ 05 мая 2010

Потому что это изменяет семя, и семя передается внутрь.

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