如何创建一个具有单线程安全静态实例的类



我刚开始使用多线程兼容软件,所以这可能是一个非常简单的问题。

如果我已经创建了一个类,我想在其中公开(它自己的(静态实例,有没有一种方法可以在线程之间同步它,使它的当前内部状态在任何地方都是相同的?如果有,实现这一点的方法是什么?

该类(在我的特定情况下(类似于内置的Random类,我不能确保所有调用都来自同一个线程,但无论哪个线程需要生成值,我都需要确保线程之间的状态保持不变。

根据要求,我将发布与类状态以及静态访问成员相关的代码

public class ChaosEngine {
// The internal state of the class that needs to be synchronized
private int _Seed;
private int _INext;
private int _INextP;
private int[] _SeedArray;
public int Seed{ get=>_Seed; set=>Reseed(value); }
// The shared instance (as it is currently written)
[ThreadStatic] private static ChaosEngine _Shared;
public static ChaosEngine Shared {
get {
if(_Shared == null)
_Shared = new ChaosEngine();
return _Shared;
}
}
}

如果需要完整的代码,可以在这里找到。

根据您链接的代码,关键操作似乎是Shared实例和Reseed(int? seed)方法的实例化。

通过将Shared实例更改为不再使用[ThreadStatic],而是初始化内联,您将拥有线程安全的实例化,并且它将保持线程安全。这也允许您删除m_Shared

public static ChaosEngine Shared = new ChaosEngine();

也就是说,对Seed的调用不是线程安全的,因为它可以更新m_Seed值。一种选择是锁定在Reseed(int? seed)内部,这意味着对Seed的任何读取都可能不是线程安全的,但对m_Seed的任何写入都将同步。

private static readonly object s_seedLocker = new object();
public int? Seed { get => m_Seed; set => Reseed(value); }
public void Reseed(int? seed)
{
lock (s_seedLocker)
{
m_Seed = seed ?? Guid.NewGuid().GetHashCode();
...
}
}

你必须确定这是否足以满足你的需求。如果没有,则需要同步对Seed的所有访问。

private static readonly object s_seedLocker = new object();
public int? Seed
{
get
{
lock (s_seedLocker)
{
return m_Seed;
}
}
set
{
lock (s_seedLocker)
{
Reseed(value);
}
}
}

请注意,在以下情况下:

var seed1 = ChaosEngine.Shared.Seed;
var engine = new ChaosEngine();
var seed2 = engine.Seed;

var seed3 = ChaosEngine.Shared.Seed;

seed1seed3将是相同的,但seed2将是唯一的。目前还不清楚这是否是你所期望的行为。

您确实能够使用以下内容将ChaosEngine的每个实例化同步到同一种子:

Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));
Task.Run(() => new ChaosEngine(ChaosEngine.Shared.Seed.Value));

其将对所有实例使用相同的公共种子值。

最新更新