2017-02-28 7 views
0

設定は以下のとおりです:DI用にAutoFacを使用するAsp.NET + MVC5。スレッドセーフティでシングルトンのプロパティを更新する

私たちは、さまざまなサービスのためのアクセストークンを管理しているクラス(シングルトン)を持っています。今や、これらのトークンは満期に近づきすぎて(10分未満)、新しいトークンを要求し、それらをリフレッシュします。あなたはインターロックは、必ず1つのスレッドだけを通過し、残りは古いトークンを取得します見ることができるように

// 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; 
} 

:私の現在の実装では、このようになります。私が思っているのは、トークンが上書きされているときに、別のスレッドが古いトークンの代わりに読み込みを試みるときに、部分的に不正な結果が得られるような奇妙な状況に終わることができますか?この実装に問題がありますか?

ありがとうございます!

+2

なぜあなたはロックを使用していませんか? – Servy

答えて

3

私にとって、この実装の最大の問題は、1つの有効期限の間にトークンを2回以上リフレッシュできることです。期限切れ状態を確認した直後であって、CompareExchange()の前にスレッドが中断された場合、最初のスレッドが再開される前に、m_interのリセットを含む別のスレッドがリフレッシュ操作を完了することができます。理論的には、これは任意の数のスレッドに発生する可能性があります。

コードの残りの部分は、コメントするのに十分ではありません。 Tokenタイプの宣言はありません。そのため、structclassかどうかは不明です。 GetCredentials()メソッドはCredentialsの値を返すと宣言されていますが、代わりにTokenの値を返します。その結果、コードは明らかに実際のコードではありません。

Tokenタイプがclassの場合、残りの実装はおそらく適切です。参照型変数は、x64プラットフォーム上であっても、原子的に割り当てることができるため、Tokenプロパティ値を取得するコードは、古いトークンまたは新しいトークンのいずれかを参照します。 (もちろん、Tokenオブジェクト自体はスレッドセーフであることが前提です。

私は個人的にはCompareExchange()を気にしません。ちょうど本格的なC#lockステートメントを使用し、それを完了してください。同期ブロックに操作全体が含まれている:有効期限を確認し、必要に応じてトークンを置き換えて、トークン値を返す。すべてはlockからである。

あなたが示したコードに基づいて、プロパティー全体にすべてのものをカプセル化し、それをpublicにするのがより理にかなっていると思います。しかし、トークン値を取得するコードがの場合に限り、のコードを同期させたコードを使って取得することができます。コードが正しいことを証明する最も簡単で確実な方法は、lockを使用することです。万が一、パフォーマンス上の問題が発生した場合は、正しい実装が困難な代替実装を検討することができます。

+0

ありがとう@Peter - これは素晴らしい読書です。ちょっとしたことを理解するために、私は実際にコード例を更新しました。あなたは正しいのですが、実際のコードではありません。トークンは文字列型です。 2つのフォローアップの質問をします。 1)スレッドが中断していると述べました。これがAsp.Netであることを考えると、実際にはその段階で中断されますか? ioコールなどはありません。なぜ実際にスレッドが中断されているのか分かりません(理論的にはあなたは絶対に正しいと思いますが) 2)逆パターンを追加すると、2回のチェックが行われます - RefreshTokenの直前に別のif文が追加されましたか? –

+0

_ "これがAsp.Netであることを考えると、その段階では本当に中断されますか?" - Windowsは、マルチスレッドの目的で定期的にすべてのスレッドを定期的に中断します。膨大な数のコア(例えば、32,64など)を有するサーバであっても、コアよりも多くのスレッドがOSの下で実行されているので、各スレッドは必然的に定期的に中断される。やむを得ないことだ。 –

+0

_ "逆パターン - 二重チェック - RefreshTokenの直前の別のif文を追加する場合は、' RefreshToken() 'を呼び出す直前にもう一度有効期限を確認することができます。リフレッシュの問題。 –

関連する問題