TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.75K subscribers
Post #2971 2.49K
День 2470. #ЗаметкиНаПолях
Вы Всё Ещё Используете new Random()? Начало
Распространённое в сети мнение об использовании Random: new Random() — зло!

Что new Random() использует системные часы для посева, и, если несколько экземпляров создаются в течение короткого промежутка времени, они генерируют идентичные результаты. Т.е. якобы:
for(var i = 0; i < 5; i++)
{
var random = new Random();
Console.WriteLine(random.Next(1,100));
}

Выведет: 42 42 42 42 42

В то время как:
for(var i = 0; i < 5; i++)
Console.WriteLine(Random.Shared.Next(1,100));

Выведет: 17 82 19 23 5

Откуда это взялось?
В .NET Framework new Random полагался на DateTime.Now.Ticks, который менялся примерно каждые 16 мс. Таким образом, в течение 16 миллисекунд вы получали одно и то же начальное значение посева, что каждый раз приводило к одному и тому же результату Next(…). Но это не так со времён .NET Core. Можете выполнить код выше и убедиться.

А что насчёт потокобезопасности?
Наивный пользователь Random, которому нужен один общий генератор случайных чисел, может поддаться искушению сделать что-то вроде следующего:
Random rng = new();
Parallel.For(0, 10, x =>
{
var value = rng.Next();
Console.WriteLine(value);
});

Несмотря на то, что этот код не является потокобезопасным, он, скорее всего, не выявит никаких проблем. До случая…

Random по умолчанию возвращает 0 при обнаружении проблемы потокобезопасности, поэтому следующий код выведет количество обнаруженных проблем:
Random rng = new();
Parallel.For(0, 10, x =>
{
var nums = new int[10_000];
for (int i = 0; i < nums.Length; ++i)
nums[i] = rng.Next();

Console.WriteLine(
$"{nums.Count(x => x == 0)} нулей");
});

Здесь уже интереснее. В .NET5 этот код показывает больше 9000 нулей в разных потоках, т.е. подавляющая часть вызовов Next сталкивается с проблемой потокобезопасности. В .NET8+ этого уже не происходит (по крайней мере, массово), однако, если предоставить посев при создании объекта Random, эта проблема вернётся:
Random rng = new(123);

Random.Shared.Next() использует потокобезопасный экземпляр (один на поток) через атрибут [ThreadStatic]. Таким образом, если вы используете последние версии .NET и вам не нужно предоставлять определённый посев, можно использовать Random.Shared (но и с new Random() скорее всего проблем не возникнет).

А что делать в .NET Framework? Об этом далее.

Окончание следует…

Источники:
-
https://steven-giesel.com/blogPost/cc2a3f52-9d35-43d2-9687-c276a2de8e07/linkedin-are-you-still-using-new-random-everywhere
-
https://andrewlock.net/building-a-thread-safe-random-implementation-for-dotnet-framework/
  • 👍 32
More from @netdeveloperdiary
  1. Sep 30, 2026Post #3356
  2. Sep 29, 2026Фото 3 (с) Анатолий Кулаков
  3. Sep 29, 2026День 2799. Конференция DotNext 2026. Часть 1 25 и 26 сентября в Москве прошла очередная ко…
  4. Sep 28, 2026День 2798. #Оффтоп Утиная Типизация в C# с Помощью Перехватчиков. Часть 2 Некоторое время…
  5. Sep 27, 2026День 2797. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Окончание Начало Продол…
  6. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →