Generating random numbers is a common task, but System.Random in older .NET versions (including .NET Framework up to 4.8.x and .NET Core prior to 6.0) presents a well-known challenge: it's not thread-safe. Accessing a single Random instance from multiple threads concurrently can lead to corrupted internal state and output sequences that are far from random (often returning zeroes).
A popular solution often found online involves wrapping System.Random within a ThreadLocal<T> or using the [ThreadStatic] attribute. Articles like Andrew Lock's post and implementations such as ThreadSafeRandomizer showcase variations of this pattern. The core idea is to create a shared Random instance used only to seed thread-specific Random instances lazily.