The CSP based RNG in CLR is just a wrapper around CryptGenRandom. Like all CSP functions, they work around a HCRYPTPROV
context handle. If I remember correctly the very first thing the provider does when entering the 'context' is to acquire a critical section that protects the 'context'. So while the function is most likely stable across threads, you really should use a separate one for each thread to avoid contention.
Update
According to this MSDN Magazine the CLR may use an instance buffer instead of a stack one, making the RNGCryptoServiceProvider unsafe across threads in future implementations:
Note that, as currently implemented in
the .NET Framework 2.0, the
parameterless constructor of
RNGCryptoServiceProvider creates
thread-safe instances. As such, we
could have created our private member
to instead have been a private static
member, and in doing so not have to
create a new RNGCryptoServiceProvider
instance for each instance of
CryptoRandom. However, this
thread-safety is not currently
documented nor is it in any way baked
into the class's contract or
interface. Given that, we haven't
relied on it for our implementation.
Note that this use is not related to the native API thread safety, the buffer issue is a CLR wrapper issue. Also, if you use the RNGCryptoServiceProvider constructor that takes a byte[], then is unsafe for sure.