C# 在并行循环之间保持ThreadLocal是个好主意还是坏主意?

C# 在并行循环之间保持ThreadLocal是个好主意还是坏主意?,c#,random,parallel-processing,thread-local,C#,Random,Parallel Processing,Thread Local,我正在尝试将ThreadLocal用于并行随机生成器。我的测试代码如下所示: class Program { static void Main() { using (MyClass myClass = new MyClass()) { for (int i = 0; i < 3; i++) { Console.WriteLine("\nPass {0}: ", i

我正在尝试将ThreadLocal用于并行随机生成器。我的测试代码如下所示:

class Program
{
    static void Main()
    {
        using (MyClass myClass = new MyClass())
        {
            for (int i = 0; i < 3; i++)
            {
                Console.WriteLine("\nPass {0}: ", i + 1);
                myClass.Execute();
            }

            Console.WriteLine("\nRandom Generators used: {0}", myClass.ListRNG.Count);
        }

        Console.WriteLine("\nPress any key...");
        Console.ReadKey();
    }
}

sealed class MyClass : IDisposable
{
    ThreadLocal<RandomGenerator> threadRNG = new ThreadLocal<RandomGenerator>(() => 
                                                    new RandomGenerator(), true);

    public IList<RandomGenerator> ListRNG { get { return threadRNG.Values; } }

    public void Execute()
    {
        Action<int> action = (i) =>
        {
            bool repeat = threadRNG.IsValueCreated;
            List<int> ints = new List<int>();
            int length = threadRNG.Value.Next(10);
            for (int j = 0; j < length; j++)
                ints.Add(threadRNG.Value.NextInt(100));
            Console.WriteLine("Action {0}. ThreadId {1}{2}. Randoms({3}): {4}", 
                i + 1, Thread.CurrentThread.ManagedThreadId, 
                repeat ? " (repeat)" : "", length, string.Join(", ", ints));
        };

        Parallel.For(0, 10, action);
    }

    public void Dispose()
    {
        threadRNG.Dispose();
    }
}

class RandomGenerator : Random
{
    public RandomGenerator() : base(Guid.NewGuid().GetHashCode())
    {
    }

    public int NextInt(int maxValue)
    {
        return base.NextDouble() >= .5 ? base.Next(maxValue) : -base.Next(maxValue);
    }
}
类程序
{
静态void Main()
{
使用(MyClass MyClass=new MyClass())
{
对于(int i=0;i<3;i++)
{
Console.WriteLine(“\nPass{0}:”,i+1);
myClass.Execute();
}
WriteLine(“\n使用的随机生成器:{0}”,myClass.ListRNG.Count);
}
Console.WriteLine(“\n按任意键…”);
Console.ReadKey();
}
}
密封类MyClass:IDisposable
{
ThreadLocal threadRNG=新ThreadLocal(()=>
新的随机生成器(),true);
公共IList ListRNG{get{return threadRNG.Values;}}
public void Execute()
{
行动行动=(i)=>
{
bool repeat=threadRNG.IsValueCreated;
List ints=新列表();
int length=threadRNG.Value.Next(10);
对于(int j=0;j=.5?base.Next(maxValue):-base.Next(maxValue);
}
}
在并行循环的多次执行之间保持ThreadLocal是一个好主意还是一个坏主意?我担心会累积未使用的RandomGenerator实例。尤其是当我有成千上万的死刑的时候

更新: 我用另一个ThreadLocal构造函数尝试了另一个版本的测试,允许访问所有值(我更改了上面的代码)


我还尝试了100000次myClass.Execute()的执行,发现只创建了17个RandomGenerator实例。因此,我认为(我希望)使用这种方法一切都正常。

如果您不要求列出所有值,则每个线程的值都以线程为根。因此,如果线程死了,它们的值就不会继续存在。你的代码很好

如果您使用
[ThreadStatic]静态随机生成器rng,它会变得更简单

注意,线程本地访问有点慢。如果让每条线程尽可能少地拉动RNG并保持一段时间(在架构上尽可能长的时间),您可能会过得更好


另外,.NETRNG速度很慢,质量也很低。考虑使用XoSHIFT和变体。

当线程完成时,您应该调用TreLealTest.Advices()。我也不想麻烦。也许这一切都有点过头了,你可以让随机线程安全吗?只需要一把锁。“在像这样的伪代码中,计时看起来不太好,但这并不重要。”HansPassant,我的代码基于和文章。我将在遗传算法中使用它,所以时间对我来说很重要。我不认为在这种情况下使用锁更好。好吧,那就做显而易见的事情。调用ThreadLocal的Dispose()方法以使您感觉良好。或者不要,因为你再也看不到它的成本了。这完全取决于您。@HansPassant,您可能还没有注意到MyClass实现了IDisposable,而我使用
将其与
一起使用,因此无需显式调用Dispose()。“因此,如果线程死亡,它们的值就不会继续存在。”我不知道,谢谢!出于这个原因,我避免使用ThreadStatic(尽管在使用静态变量后,我可以为其分配null)。我会考虑使用XORSHIFT。再次感谢你的回答!