--- title: 时间戳精度单线程下机器得到相同号 tags: - 时间格式 - 精度 cover: 'https://picsum.photos/400' abbrlink: c2df6508 date: 2023-08-19 20:12:09 --- \[C# Random.Next() 生成了相同的数字_c# new random().next()_小鹰信息技术服务部的博客-CSDN博客\](https://blog.csdn.net/zhouyingge1104/article/details/90729586) \[Random 类 (System) \| Microsoft Learn\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0#Multiple) 官方文档已经说了: - 构造 \[Random(Int32)\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random.-ctor?view=net-7.0#system-random-ctor(system-int32)) 函数使用你提供的显式种子值。 - 构造 \[Random()\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random.-ctor?view=net-7.0#system-random-ctor) 函数使用默认种子值。 这是实例化随机数生成器的最常见方法。 但是,仅在.NET Framework,因为时钟具有有限分辨率,因此使用无参数构造函数创建紧\[Random\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0)接的不同对象会创建生成相同随机数序列的随机数生成器。 以下示例演示了在 .NET Framework 应用程序中连续实例化的两\[Random\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0)个对象如何生成一系列相同的随机数。 在大多数 Windows 系统上, \[Random\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0) 在 15 毫秒内创建的对象可能具有相同的种子值。 使用时间:但时间的分辨率低于15毫秒,程序太快,得到就是相同的值; ### 避免多个实例化 在.NET Framework,在紧密循环或快速连续中初始化两个随机数生成器会创建两个可以生成相同序列随机数的随机数生成器。 在大多数情况下,这不是开发人员的意图,并可能导致性能问题,因为实例化和初始化随机数生成器是一个相对昂贵的过程。 为了提高性能,并避免无意中创建生成相同数值序列的单独随机数生成器,建议创建一个 \[Random\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0) 对象以随时间推移生成多个随机数,而不是创建新 \[Random\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0) 对象来生成一个随机数。 但是, \[Random\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0) 类不是线程安全的。 如果从多个线程调用 \[Random\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0) 方法,请遵循下一部分中讨论的准则。 因为伪随机:初始化序列时,太快可能是同一个 不能使用静态,但它是线程不安全的 如果应用从多个线程调用 \[Random\](https://learn.microsoft.com/zh-cn/dotnet/api/system.random?view=net-7.0) 方法,则必须使用同步对象来确保一次只能有一个线程访问随机数生成器。 --- 雪花算法 因为存在润秒,及时间同步及回拨问题; 所以依赖时间机器也是很可能得到相同序列!!1 \[记一次"雪花算法"造成的生产事故的排查记录 \| HeapDump性能社区\](https://heapdump.cn/article/4934878) When you create a \`Random\` object, it's seeded with a value from the system clock. If you create \`Random\` instances too close in time, they will all be seeded with the same random sequence. --- 扩展 1. 自旋锁:自旋锁(Spinlock)是最简单的线程锁,基于原子操作实现,它使用一个数值来表示锁是否已经被获取,0表示未被获取,1表示已经获取,获取锁时会先使用原子操作设置数值为1,然后检查修改前的值是否为0,如果为0则代表获取成功,否则继续重试直到成功为止,释放锁时会设置数值为0,其他正在获取锁的线程会在下一次重试时成功获取,使用原子操作的原因是,它可以保证多个线程同时把数值0修改到1时,只有一个线程可以观察到修改前的值为0,其他线程观察到修改前的值为1 .NET 可以使用以下的类实现自旋锁: System.Threading.Thread.SpinWait System.Threading.SpinWait System.Threading.SpinLock 使用自旋锁有个需要注意的问题,自旋锁保护的代码应该在非常短的时间内执行完毕,如果代码长时间运行则其他需要获取锁的线程会不断重试并占用逻辑核心,影响其他线程运行,此外,如果 CPU 只有一个逻辑核心,自旋锁在获取失败时应该立刻调用 Thread.Yield 函数提示操作系统切换到其他线程,因为一个逻辑核心同一时间只能运行一个线程,在切换线程之前其他线程没有机会运行,也就是切换线程之前自旋锁没有机会被释放 \[lock 语句 - 同步对共享资源的线程访问 \| Microsoft Learn\](https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/lock) ## 准则 当同步对共享资源的线程访问时,请锁定专用对象实例(例如,\`private readonly object balanceLock = new object();\`)或另一个不太可能被代码无关部分用作 lock 对象的实例。 避免对不同的共享资源使用相同的 lock 对象实例,因为这可能导致死锁或锁争用。 具体而言,请避免将以下实例用作 lock 对象: - \`this\`(调用方可能将其用作 lock)。 - \[Type\](https://learn.microsoft.com/zh-cn/dotnet/api/system.type) 实例(可以通过 \[typeof\](https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/operators/type-testing-and-cast#typeof-operator) 运算符或反射获取)。 - 字符串实例,包括字符串字面量,(这些可能是\[暂存的\](https://learn.microsoft.com/zh-cn/dotnet/api/system.string.intern#remarks))。 尽可能缩短持有锁的时间,以减少锁争用。 \[lock 语句 - 同步对共享资源的线程访问 \| Microsoft Learn\](https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/statements/lock)