2010-01-18 70 views
2

我有一些只讀數據,我想要初始化,然後以線程安全方式定期重新初始化。對於初始化,我拉入了Joe Duffy的LazyInit and LazyInitOnceOnly structs as detailed in his blog,它使用了Double-Checked鎖定模式。所以我目前執行的吸氣劑只是環繞他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; 
    } 
    } 
} 

當數據被確立爲擺脫日期時,應該返回舊數據,但新數據應在單獨的線程上準備好,並在準備就緒時進行交換 - 以免阻止調用者。從數據中的所有後續讀取應該返回舊值直到更新。

所以我認爲在ReInitialise()方法排隊這樣的新線程:

() => { 
    //assume constructor pulls in all the data and sets timestamp 
    _sharedData = new MyData(); 
} 

的_sharedData覆蓋在該線程將原子發生,所以這是很好。但是通過這段代碼,直到重建完成,所有後續讀取都會嘗試並觸發線程重建 - 因爲它們正在讀取舊的_sharedData的TimeStamp屬性。

確保只觸發一次重建的最佳方法是什麼?

+0

所以你希望重建發生在一個新的,衍生出來的後臺工作線程和主客戶線程上,以便直接返回**舊**數據(即使getter知道它是舊的)直到在後臺線程上重建做完了??只是檢查這裏的要求.... – martinr 2010-01-18 12:45:39

+0

是的,沒錯 - 對不起,我在我的問題中有點鬆了一口氣!這是我的一個詛咒。 – 2010-01-18 12:51:44

+0

已更新文本,使其更清晰一點 - 謝謝;) – 2010-01-18 12:55:42

回答

1

或者,(再次不使用LazyInit的東西)在構造函數中設置Int32 m_buildState = 0。將m_publishData成員設置爲null(在此方法中,這是您的自定義數據對象類型而不是LazyInit對象類型)爲null。

在getter中,設置d = Interlocked.CompareExchange(ref m_buildState,1,0)。這裏d是一個局部決策變量。

如果d == 2檢查是否發生數據更新超時;如果是這樣,下次測試Interlocked.CompareExchange(ref 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,然後發出信號事件。

我在這裏假設重建線程重建所花費的時間不夠長,無法進行另一次重建,並且不需要併發操作的超時。如果這些不是安全的假設,則需要更多的檢查。

+0

你的假設是正確的 - 重建時間應該在1-5秒的範圍內,而超時時間可能會超過1小時。 非常光禿禿的骨頭 - 就像它! – 2010-01-18 17:06:35

+0

雖然在技術上ReaderWriterLockSlim答案是正確的,但我喜歡這個答案,因爲它解決了在狀態變量之外有任何長期運行的資源的問題 - 我之前爲其他場景做過類似的事情。謝謝你的幫助 :) – 2010-01-19 16:39:44

1

看起來有這樣一個標準的類:ReaderWriterLockSlim,或者在舊版本的.NET ReaderWriterLock上。

ReaderWriterLockSlim看起來是ReaderWriteLock的更快版本。

This stackoverflow answer聲稱新的Slim類是基於Vance Morrison's design

雖然你可以(只是非常輕微地)改善他列出的性能代碼(通過內聯他的EnterMyLock,ExitMyLock和EnterMyLockSpin函數),但它可能不值得這樣做。

+0

+1 - ReadWriterLockSlim和我是好朋友,我以前用它來做這種模式。我之所以沒有還有;)是因爲我沒有保護整個數據結構(僅僅是初始化和重新初始化)。因此使用具有平臺手柄的IDisposable對象似乎有點沉重。 如果沒有其他的東西出現,我可能會走這條路。謝謝你的幫助! – 2010-01-18 15:26:57