Если у вас действительно параллельные запросы, то DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).Ticks * 100
является константой для этих запросов.Тогда у вас останется random.Next(100)
, и тогда столкновение не займет много времени.Тривиальным (но не идеальным) вариантом было бы просто сделать random.Next()
.
Лучшей идеей было бы следующее:
[ThreadStatic]
private static Random __random = new Random();
private static int shift = 32;
private static long counter = 0L;
public long GenerateNextNonce()
{
var major = ++counter << shift;
var minor = (DateTime.UtcNow.Ticks ^ __random.Next()) & (1L << shift - 1);
return major + minor;
}
++counter
обеспечивает увеличениепоследовательность чисел - одного этого было бы достаточно для создания одноразового номера, но это очень предсказуемо, поэтому было бы открыто для взлома.
Вычисление DateTime.UtcNow.Ticks ^ __random.Next()
обеспечивает довольно случайное число, которое не зависит исключительно от реализацииRandom
, таким образом, это гарантирует, что это число очень непредсказуемо , но не обязательно увеличивается.
Использование значения shift
гарантирует, что значение counter
смещено в«высокого класса» или большая часть номера.Вызов (DateTime.UtcNow.Ticks ^ __random.Next()) & (1L << shift - 1)
усекает старшие биты случайной части одноразового номера, гарантируя, что младшее значение не разделяет биты с старшим номером.
Я запустил это со значением shift
32
и произвел 100_000_000
значений и исчерпал только менее 5% доступных чисел в направлении long.MaxValue
.Пока вы производите менее 2 миллиардов одноразовых номеров, вы должны быть хорошими.Если вы хотите больше, уменьшите shift
.