C# 线程安全共享对象过期和重新初始化
我有一些只读数据要初始化,然后以线程安全的方式定期重新初始化。对于初始化,我使用了Joe Duffy's,它使用双重检查锁定模式。因此,我当前的getter实现只是围绕他的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() {
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();
}