[英]Updating property of a singleton with Thread Safety
我們的設置是:Asp.NET + MVC5使用AutoFac進行DI。
我們有一個類(它是一個單例),它管理各種服務的訪問令牌。 時不時地,這些令牌過於接近到期(不到10分鍾),我們請求新令牌,刷新它們。 我目前的實現如下:
// member int used for interlocking
int m_inter = 0;
private string Token { get; set; }
private DateTimeOffset TokenExpiry { get; set; }
public SingletonClassConstructor()
{
// Make sure the Token has some value.
RefreshToken();
}
public string GetCredentials()
{
if ((TokenExpiry - DateTimeOffset.UTCNow).TotalMinutes < 10)
{
if (Interlocked.CompareExchange(ref m_inter, 1, 0) == 0)
{
RefreshToken();
m_inter = 0;
}
}
return Token;
}
private void RefreshToken()
{
// Call some stuff
Token = X.Result().Token;
TokenExpiry = X.Result().Expiry;
}
正如您所看到的,Interlocked確保只有一個線程通過,其余線程獲得舊令牌。 我想知道的是 - 我們能否最終處於一種奇怪的情況,即當令牌被覆蓋時,另一個線程試圖讀取而不是舊令牌,會得到部分搞砸的結果? 這個實現有什么問題嗎?
謝謝!
對我而言,此實現的最大問題是您可以在一個有效期內刷新令牌兩次或更多次。 如果線程在檢查到期條件之后但在CompareExchange()
之前被CompareExchange()
,那么另一個線程可以在第一個線程恢復之前完成整個刷新操作,包括重置m_inter
。 這理論上可以發生在任意多個線程上。
您的代碼的其余部分不夠具體,無法發表評論。 沒有Token
類型的聲明,因此不清楚這是一個struct
還是class
。 並且您的GetCredentials()
方法被聲明為返回Credentials
值,而是返回Token
值,因此代碼顯然甚至不是真正的代碼。
如果Token
類型是一個class
,那么其余的實現可能就好了。 即使在x64平台上,也可以原子方式分配引用類型變量,因此檢索Token
屬性值的代碼將看到舊令牌或新令牌,而不是某些損壞的中間狀態。 (當然,我假設Token
對象本身是線程安全的,最好是因為它是不可變的。)
就個人而言,我不打擾CompareExchange()
。 只需使用完整的C# lock
語句即可完成。 包含synchronized塊中的整個操作:檢查到期時間,必要時替換令牌,並從lock
返回令牌值。
根據您展示的代碼,我認為將整個事物封裝在屬性本身並將其public
更有意義。 但是不管怎樣,只要代碼檢索令牌值只能通過這部分同步代碼獲得它,證明代碼正確的最簡單,最可靠的方法就是使用lock
。 萬一你看到性能問題,那么你可以考慮更難以實現的替代實現。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.