【发布时间】: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 线程安全?它只需要一把锁。在这样的假代码中,时间看起来不会那么好,但这并不重要。
-
好的,那就做显而易见的事情。调用 ThreadLocal 的 Dispose() 方法让你感觉良好。或者不要,因为你永远不会看到它的成本。这完全取决于您。
-
@HansPassant,你可能没有注意到 MyClass 实现了 IDisposable 并且我将它与
usingstatement 一起使用,因此无需显式调用 Dispose()。
标签: c# random parallel-processing thread-local