Я использую.NET для создания искусственной жизненной программы, и я использую псевдослучайный класс C#, определенный в Singleton. Идея состоит в том, что, если я использую тот же генератор случайных чисел всюду по приложению, я мог бы просто сохранить семя и затем перезагрузить от семени для перевычислений определенного интересного выполнения.
public sealed class RandomNumberGenerator : Random
{
private static readonly RandomNumberGenerator instance = new RandomNumberGenerator();
RandomNumberGenerator()
{
}
public static RandomNumberGenerator Instance
{
get
{
return instance;
}
}
}
Я также хотел метод, который мог дать мне два различных случайных числа.
public static Tuple<int, int> TwoDifferentRandomNumbers(this Random rnd, int minValue, int maxValue)
{
if (minValue >= maxValue)
throw new ArgumentOutOfRangeException("maxValue", "maxValue must be greater than minValue");
if (minValue + 1 == maxValue)
return Tuple.Create<int, int>(minValue, maxValue);
int rnd1 = rnd.Next(minValue, maxValue);
int rnd2 = rnd.Next(minValue, maxValue);
while (rnd1 == rnd2)
{
rnd2 = rnd.Next(minValue, maxValue);
}
return Tuple.Create<int, int>(rnd1, rnd2);
}
Проблема иногда - это rnd.Next(minValue,maxValue
всегда возвраты minValue
. Если я устанавливаю контрольные точки в этой точке и пытаюсь создать двойное и установить ее на rnd.NextDouble()
, это возвращается 0.0. Кто-либо знает, почему это происходит?
Я знаю, что это - генератор псевдослучайных чисел, но откровенно говоря, я не ожидал, что это заблокирует в 0. К генератору случайных чисел получают доступ от нескольких потоков... это могло быть источником проблемы?
РЕДАКТИРОВАНИЕ: Спасибо, проблема закончила тем, что была потокобезопасностью.
Это - новая версия класса.
public sealed class RandomNumberGenerator : Random
{
private static Random _global = new Random();
[ThreadStatic]
private static Random _localInstance;
RandomNumberGenerator()
{
}
public static Random Instance
{
get
{
Random inst = _localInstance;
if (inst == null)
{
int seed;
lock (_global) seed = _global.Next();
_localInstance = inst = new Random(seed);
}
return _localInstance;
}
}
}
Если вы используете только один RNG для нескольких потоков, даже если вы сохраните начальное число, вы не сможете сгенерировать то же самое. числа при следующем запуске приложения, потому что вы не будете уверены, что вызовы ГСЧ из разных потоков будут в том же порядке.
Если у вас есть фиксированное / известное количество потоков, создайте ГСЧ для каждого потока и сохраните каждое начальное число.
Забудьте то, что я только что сказал, если вы на 100% уверены, что каждый поток будет вызывать ГСЧ в том же порядке, что и в прошлый раз, если вы используете одно и то же начальное число.
Идея состоит в том, что если я использую один и тот же генератор случайных чисел во всем приложении, я могу просто сохранить начальное число, а затем перезагрузить его для повторного вычисления определенного интересный пробег.
На самом деле для этого не требуется одноэлементный экземпляр ГСЧ. Если вы инициализируете два отдельных экземпляра Random
одним и тем же начальным числом
, они будут производить точно такую же последовательность.
Мой совет: сохраните семя, но избавьтесь от синглтона.
Класс Random
не является потокобезопасным.
Вы должны сделать свой статический
экземпляр [ThreadStatic]
или защитить его блокировкой.
Мне даже не нужно искать класс Random, чтобы понять, что "все методы экземпляра этого класса не потокобезопасны". Это относится ко всем классам .NET, за очень немногими исключениями.
Так что да, это многопоточность. Но вы также не упомянули о проверке того, что MaxValue > MinValue.