C# 线程安全共享对象过期和重新初始化

C# 线程安全共享对象过期和重新初始化,c#,.net,multithreading,C#,.net,Multithreading,我有一些只读数据要初始化,然后以线程安全的方式定期重新初始化。对于初始化,我使用了Joe Duffy's,它使用双重检查锁定模式。因此,我当前的getter实现只是围绕他的LazyInitOnceOnly.Value属性,并为超时检查增加了空间: 因此,代码如下: public class MyData { public DateTime TimeStamp { get; set; } //actual shared data ommitted public MyData() {

我有一些只读数据要初始化,然后以线程安全的方式定期重新初始化。对于初始化,我使用了Joe Duffy's,它使用双重检查锁定模式。因此,我当前的getter实现只是围绕他的
LazyInitOnceOnly.Value
属性,并为超时检查增加了空间:

因此,代码如下:

public class MyData {
  public DateTime TimeStamp { get; set; }
  //actual shared data ommitted

  public MyData() { TimeStamp = DateTime.Now; }
}

public SharedDataContainer
{
  //data to be initialised thread-safe, and shared.
  //assume delegate passed on construction simply 'new's the object,
  private LazyInitOnceOnly<MyData> _sharedDataInit;
  //receives the result from the _sharedDataInit.Value property
  private MyData _sharedData;
  //time-out and reinitialise after 24 hours
  private TimeSpan _timeOut = new TimeSpan(24,0,0);

  public MyData SharedData
  {
    get{
      //slight adaptation of the use of the LazyInitOnceOnly struct - 
      //because we want to replace _sharedData later after an expiry time out.
      if(_sharedData == null)
        _sharedData = _sharedDataInit.Value;
      //need best ideas for this bit:
      if((DateTime.Now - _sharedData.TimeStamp) > _timeOut)
      {
        ReInitialise();
      }
      return _sharedData;
    }
  }
}
线程中的_sharedData覆盖将以原子方式发生,因此这很好。但是对于这段代码,在重建完成之前,所有后续读取都将尝试并触发线程重建——因为它们正在读取旧的_sharedData的TimeStamp属性


确保只触发一次重建的最佳方法是什么?

执行此操作似乎有一个标准类:ReaderWriterLockSlim或.NET ReaderWriterLock的旧版本

ReaderWriterLockSlim似乎是ReaderWriterLock的更快版本

声称新的Slim类基于

虽然您可以(只需非常轻微地)改进他列出的代码的性能(通过内联他的EnterMyLock、ExitMyLock和EnterMyLockSpin函数),但这样做可能不值得。

或者,(同样不使用Lazyint工具)在构造函数中设置Int32 m_buildState=0。将m_publishData成员(在这种方法中,这是您的自定义数据对象类型,而不是LazyInit对象类型)设置为null

在getter中,设置d=Interlocked.compareeexchange(参考m_buildState,1,0)。这里d是一个局部决策变量

如果d==2,检查数据更新超时是否发生;如果是,下一个测试是否联锁。比较交换(参考m_buildState,3,2)==2。如果这是真的,启动后台线程来重建数据。返回m_publishData。(后台重建线程的最后一步必须首先更新m_publishData,然后将m_buildState设置为2。)

如果d==3,则返回m_publishData成员

如果d==1,等待d>=2。要以最佳方式执行此操作,请等待事件发生(如果要优化代码,可以先旋转等待/测试d>=2一段时间)。然后返回m_publishData

如果d==0,在当前线程上重新生成,然后将m_publishData设置为数据对象,然后将m_buildState设置为2,然后发送事件信号


我在这里假设,重建线程重建所花费的时间不够长,不需要再次重建,并且不需要并发操作超时。如果这些不是安全的假设,则需要进行更多的检查。

因此,您希望在新生成的后台工作线程上进行重建,并且在后台线程上的重建完成之前,主客户端线程立即返回旧数据(即使getter知道它是旧的)??只是在这里检查一下要求……是的,没错——对不起,我的问题有点含糊其辞!这是我的诅咒。请更新文本以使其更加清晰-谢谢;)+1-ReadWriterLockSlim和我是好朋友,我以前用过这种模式。我还没有找到的原因;)因为我没有保护整个数据结构(只是初始化和重新初始化);因此,使用具有平台句柄的IDisposable对象似乎有点沉重。不过,如果没有其他问题,我可能会走这条路。谢谢你的帮助!您的假设是正确的-重建时间应在1-5秒范围内,而超时时间可能大于1小时。非常裸露的骨头-喜欢!虽然ReaderWriterLockSlim的答案在技术上是正确的,但我喜欢这个答案,因为它解决了在状态变量之外有任何长时间运行的资源的问题——我以前为不同的场景做过类似的事情。谢谢你的帮助:)
() => {
  //assume constructor pulls in all the data and sets timestamp
  _sharedData = new MyData();
}