Ну, rand_r функция, как предполагается, ориентированная на многопотоковое исполнение функция. Однако его реализацией, я не могу полагать, что она могла заставить себя не измениться другими потоками. Предположим, что два потока вызовут rand_r в то же время с тем же переменным семенем. Таким образом, гонка чтения-записи произойдет. Код 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;
}
Вы можете представить себе три уровня безопасности потоков, которые я обозначу здесь для удобства.
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 соглашение иное, возможно, потому, что внутренне синхронизированные структуры данных с самого начала довольно редки. В контексте C вы можете спросить «являются ли файловые дескрипторы потокобезопасными?» И говорить об уровне 3, но когда спрашиваете «является ли эта функция потокобезопасной?» обычно означает уровень 2.
Потому что он изменяет начальное число, и оно передается.
rand_r
является потокобезопасным, потому что функция полностью чистая. Он не читает и не изменяет никаких состояний, кроме аргументов. Поэтому его можно безопасно вызывать одновременно.
Это отличается от большинства функций rand
, которые хранят состояние (начальное значение) в глобальной переменной.
Предположим, что два потока одновременно вызовут rand_r с одним и тем же начальным значением переменной.
Я предполагаю, что вы имеете в виду что-то вроде этого
int globalSeed;
//thread 1
rand_r(&globalSeed);
//thread 2
rand_r(&globalSeed);
Это не означает, что функция не является поточно-ориентированной, это просто означает, что вы используете ее небезопасным способом, предоставляя выходной параметр, к которому можно получить доступ / изменено другим потоком.
Это то же самое, что записать результат функции в глобальную переменную, к которой может получить доступ / изменить другой поток. Это не значит, что функция не безопасна для потоков, это означает, что ваш код не безопасен для потоков.