2017-02-28 7 views
0

설정은 다음과 같습니다. DI 용 AutoFac을 사용하는 Asp.NET + MVC5.스레드 안전성을 사용하여 싱글 톤 속성 업데이트

우리는 다양한 서비스를위한 액세스 토큰을 관리하는 클래스 (싱글 톤)를 가지고 있습니다. 지금은이 토큰이 너무 오래 만기가되고 (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를 통해 알 수 있듯이 하나의 스레드 만 통과하고 나머지는 이전 토큰을 가져옵니다. 내가 궁금해하는 점은 - Token이 덮어 쓰여질 때, 다른 스레드가 오래된 토큰 대신 읽기를 시도 할 때 부분적으로 망가진 결과를 얻는 이상한 상황에 처하게 될까요? 이 구현에 문제가 있습니까?

감사합니다.

+2

왜 잠금을 사용하지 않는거야? – Servy

답변

3

이 구현에서 가장 큰 문제는 단일 만료 기간 동안 두 번 이상 토큰을 새로 고칠 수 있다는 것입니다. 스레드가 만료 조건을 확인한 직후에 CompareExchange() 이전에 일시 중단 된 경우, 첫 번째 스레드가 다시 시작되기 전에 m_inter 재설정을 포함하여 다른 스레드가 새로 고침 작업을 완료 할 수 있습니다. 이것은 이론적으로 임의의 많은 스레드에 발생할 수 있습니다.

코드의 나머지 부분에 대한 설명이 충분하지 않습니다. Token 유형의 신고가 없으므로 struct 또는 class인지 여부는 분명하지 않습니다. GetCredentials() 메서드는 Credentials 값을 반환하지만 대신 Token 값을 반환하므로 코드가 실제 코드가 아닙니다.

Token 유형이 class 인 경우 나머지 구현은 괜찮을 것입니다. 참조 유형 변수는 심지어 x64 플랫폼에서도 원자 적으로 할당 될 수 있으므로 Token 속성 값을 검색하는 코드는 기존 토큰 또는 새 토큰 중 하나가 손상된 중간 상태가 아닌 코드를 보게됩니다. (물론, Token 객체 자체가 스레드로부터 안전하다고 가정합니다.

개인적으로, 나는 CompareExchange()으로 신경 쓰지 않을 것입니다. 그냥 C# lock 성명을 작성하고 처리하십시오. 동기화 된 블록에 전체 작업을 포함하십시오. 만료 시간을 확인하고, 필요한 경우 토큰을 바꾸고, 토큰 값을 반환하십시오 (모두 lock 내에서 발생).

당신이 보여준 코드를 기반으로, 나는 속성 자체에 모든 것을 캡슐화하고 그 것을 만들어주는 것이 더 합리적이라고 생각할 것입니다. public. 그러나 한 가지 또는 다른 방법으로 코드가 토큰 값을 가져올 수있는 한 코드를 사용하면 코드가 정확하고 쉽고 안정적으로이 섹션을 통해 얻을 수 있으므로 lock을 사용하는 것이 좋습니다. 드문 경우이지만 성능 문제가 있음을 알게되면 올바른 구현이 어려운 대체 구현을 고려할 수 있습니다.

+0

감사합니다. @Peter - 이것은 놀라운 읽을 거리입니다. 조금 더 나은 것을 이해하기 위해 실제로 코드 예제를 업데이트했습니다. 실제로 맞습니까? 실제 코드가 아닙니다. 토큰은 유형 문자열입니다. 2 개의 후속 질문을하고 싶습니다. 1) 스레드가 일시 중단되었다고 말씀하셨습니다. 이것이 Asp.Net이라는 것을 감안할 때 실제로 그 단계에서 정지 될 것인가? 아니 io 전화 등 ..이론적으로 말하자면 당신은 절대적으로 맞습니다. 2) 반 패턴을 추가한다면 - 이중 검사 - RefreshToken 바로 전에 다른 if 문을 사용할 수 있습니까? –

+0

_ "이것이 Asp.Net이라는 것을 감안할 때 실제로 그 단계에서 일시 중지됩니까?"- Windows는 멀티 태스킹 목적으로 주기적으로 모든 스레드를 주기적으로 일시 중단합니다. 엄청난 수의 코어 (예 : 32, 64 등)가있는 서버조차도 코어가있는 것보다 OS에서 더 많은 스레드가 계속 실행되므로 모든 스레드가 반드시 주기적으로 일시 중단됩니다. 피할 수없는 일입니다. –

+0

_ "반 패턴 - 이중 검사 - RefreshToken 바로 앞에있는 다른 if 문을 추가하면"RefreshToken() "을 호출하기 직전에 만료 시간을 다시 확인할 수 있습니다. - 새로 고침 문제. –