【问题标题】:Is it a good/bad idea to keep ThreadLocal between parallel loops?在并行循环之间保持 ThreadLocal 是好还是坏?
【发布时间】:2017-03-12 15:20:21
【问题描述】:

我正在尝试将 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);
    }
}

在并行循环的多次执行之间保持 ThreadLocal 是好还是坏?我担心可能会累积未使用的 RandomGenerator 实例。尤其是当我有成千上万的处决时。

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

我还尝试了 100 000 次 myClass.Execute() 的执行,发现只创建了 17 个 RandomGenerator 实例。所以我认为(我希望)这种方法一切正常。

【问题讨论】:

  • 你应该在线程完成时调用 ThreadLocal.Dispose() 。我也不会打扰。也许这有点矫枉过正,您可以使 Random 线程安全?它只需要一把锁。在这样的假代码中,时间看起来不会那么好,但这并不重要。
  • @HansPassant,我的代码基于thisthis 文章。我将在遗传算法中使用它,所以时间对我来说很重要。我不认为在这种情况下使用锁更好。
  • 好的,那就做显而易见的事情。调用 ThreadLocal 的 Dispose() 方法让你感觉良好。或者不要,因为你永远不会看到它的成本。这完全取决于您。
  • @HansPassant,你可能没有注意到 MyClass 实现了 IDisposable 并且我将它与 using statement 一起使用,因此无需显式调用 Dispose()。

标签: c# random parallel-processing thread-local


【解决方案1】:

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

如果您使用[ThreadStatic] static RandomGenerator rng;,它会变得更加简单。

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

此外,.NET RNG 真的很慢而且质量很差。考虑使用 XorShift 及其变体。

【讨论】:

  • “因此,如果线程死了,它们就不会保持它们的值。”我不知道,谢谢!出于这个原因,我避免使用 ThreadStatic(尽管无论如何我可以在静态变量使用后为其分配空值)。我会考虑使用 XorShift。再次感谢您的回答!
猜你喜欢
  • 1970-01-01
  • 2016-07-19
  • 2011-11-04
  • 2015-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-01
  • 1970-01-01
相关资源
最近更新 更多