2009-10-07 2 views
11

Interlocked. * 동기화 메서드를 사용하여 DateTime 변수를 업데이트 할 수 있습니까?인터록을 사용한 스레드 안전 DateTime 업데이트 *

메모리에 마지막 터치 타임 스탬프를 유지하고 싶습니다. 여러 http 스레드가 마지막 터치 DateTime 변수를 업데이트합니다.

DateTime 변수는 업데이트되는 대신 값으로 대체되는 값 형식입니다. 예, 당신이 할 수있는

내가 가지고 올 수있는 가장 긴

class x 
{ 
    long _lastHit; 

    void Touch() 
    { 
    Interlocked.Exchange(ref _lastHit, DateTime.Now.Ticks); 
    } 
} 
+0

저는 이것이 이전 질문이지만 CPU의 명령 실행 순서를 보장 할 수 없다고 생각하는 것을 어떻게 생각합니까? 약간 더 새로운 타임 스탬프를 오래된 것으로 교체하지 않는다는 것을 알고 계십니까? (우리는 여기서 나노 초를 말하고 있습니다.) –

답변

2

에 총 진드기와 타임 스탬프를 유지하는 것입니다. 가장 큰 문제는 DateTime.Ticks가 ~ 20ms의 해상도 밖에 가질 수 없다는 것입니다. 따라서 DateTime last 또는 long ticks 변수를 유지하는 것이 중요하지 않습니다. 그러나 DateTime에 Exchange의 과부하가 없으므로 long을 사용해야합니다.

+0

@ Adam - 대답을 삭제하기 전에 말했던 것처럼. 휘발성 키워드를 언급 해 주셔서 감사합니다. 그러나 그것은 버그가있는 것만으로는 스레드 세이프 읽기는 제공하지만 업데이트는 제공하지 않습니다. – camelCase

+0

@Henk -이 경우 20ms입니다. 터치 타임 스탬프의 유스 케이스는 멀티 테넌트 웹 시스템입니다. 오전 2시에 DB 인스턴스에 호스팅 된 모든 사용자가 데이터 수정 스크립트를 실행하기 전에 시스템 밖으로 있는지 확인해야합니다. – camelCase

+0

마지막 업데이트의 정확성을 원하는 경우 System.Diagnostics.Stopwatch.GetTimestamp 메서드 http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.gettimestamp.aspx를 사용할 수 있습니다. – kevindaub

0

편집 : 아래 @romkyns 의견에 따라 [감사합니다]

코드는 32 비트 시스템에서 실행됩니다. 그러면 64 비트 길이는 두 개의 원자 적 연산으로 메모리에 기록 될 것입니다. 은 컨텍스트 스위치로 인터럽트됩니다. 일반적으로이 문제를 해결해야합니다.

그러나이 특정 시나리오의 경우 (시간 틱을 나타내는 긴 값 작성) 문제는 너무 매우과 다룰 가치가 없다고 주장 할 수 있습니다. 두 번째 32 틱마다 한 번씩 두 번째 분할), 상위 단어 (32 비트)의 값은 임의의 두 개의 동시 쓰기에 대해 동일 할 것입니다 ... 매우 희박한 경우에도 두 개의 동시 쓰기 이 경계를 확장하여 동시에 서로를 방해하고 하나의 단어와 다른 단어의 낮은 단어를 얻습니다. 또한이 값을 매초마다 읽지 않는 한 다음 글은 어쨌든 문제를 해결할 것이며 아무런 해를 끼치 지 않을 것입니다. 끝난. 그러나이 방법을 사용하면 아무리 나쁜 사례라도 될지라도 40 억 틱 (tick)마다 한 번 잘못된 값을 가져 오는 매우 슬림하지만 가능한 시나리오를 허용합니다. (그리고 그 버그를 재현하려는 행운을 빕니다 ...)

(현재로서는 훨씬 더 많은 가능성이 있지만 은 아니지만이 보장되지 않음) 64 비트 시스템에서 실행중인 경우 64 비트 메모리 슬롯의 값은 원자 적으로 기록되며 동시성에 대해 걱정할 필요가 없습니다. 일부 스레드가 다른 스레드에 의해 인터럽트 될 수있는 일부 처리 중에 유효하지 않은 상태 인 경우 불변 프로그램이있는 경우에만 경쟁 조건이 발생할 수 있습니다. 당신이하고있는 일이 lastTouch DateTime 변수 (메모리 위치)에 쓰고 있다면 걱정할 invlaid 불변 변수가 없으므로 동시 액세스에 대해 걱정할 필요가 없습니다.

+1

당신은 동시성과 논리에 대해 옳았 습니다만, 'long'을 쓰는 것은 원 자성이 아니므로 Lock 또는 Interlocked가 필요합니다. –

+1

@Charles - Henk이 말하기를 부분적으로 업데이트 된 긴 값을 읽는 것에 대해 우려하고 있습니다. 매우 감사할만한 상황. – camelCase

+4

camelCase, "가능성 낮음"은 찾기 어렵고 디버그하는 것을 의미합니다. –

6

옵션 1 : InterlockedDateTime.ToBinary()long를 사용합니다. Interlocked가 이미 원자 적 업데이트를 보장하기 때문에 volatile (실제로는 경고가 표시됩니다)이 필요하지 않습니다. 이 방법으로 DateTime의 정확한 값을 얻을 수 있습니다.

DateTime GetLastHit() 
{ 
    long lastHit = Interlocked.CompareExchange(ref _lastHit, 0, 0); 
    return DateTime.FromBinary(lastHit); 
} 

이것은 _lastHit의 값을 반환하고, 그것을 0 0 스왑 인 경우 (즉, 값 원자를 읽을 이외의 아무것도하지 않습니다) :

long _lastHit; 

void Touch() 
{ 
    Interlocked.Exchange(ref _lastHit, DateTime.Now.ToBinary()); 
} 

은 원자이를 읽을 수 있습니다.

단순히 변수가 휘발성으로 표시되지 않았기 때문에 읽기가 쉽지 않습니다. 따라서 후속 읽기는 캐시 된 값을 재사용 할 수 있습니다. 휘발성을 결합하면 & 연동 가능성이 여기 (나는 완전히 확실하지 않지만 연동되지 않은 쓰기가 비 연동 읽기를 수행하는 다른 코어에 의해 일관성없는 상태로 보일 수 없다고 생각합니다). 하지만 이렇게하면 두 가지 기술을 결합하여 경고와 코드 냄새가 날 것입니다.

옵션 2 : 자물쇠를 사용하십시오. 이 경우 인터록 방식이 더 효과적이기 때문에 이러한 상황에서는 바람직하지 않습니다. 하지만 당신은 올바른 유형을 저장할 수 있으며, 소폭 명확입니다 :

DateTime _lastHit; 
object _lock = new object(); 

void Touch() 
{ 
    lock (_lock) 
     _lastHit = DateTime.Now; 
} 

당신은 도이 값을 읽기 위해 잠금 장치를 사용해야합니다! 덧붙여, 상호 배제 외에도 잠금은 캐시 된 값을 볼 수 없으며 읽기/쓰기를 재정렬 할 수 없도록합니다.

비표준 : volatile으로 표시할지 여부는 선택하지 말고 그냥 값을 쓰십시오. 이것은 잘못된 것입니다 - 당신이 값을 읽어 본 적이 경우에도 32 비트 컴퓨터에 당신의 쓰기가 당신이 손상된 값을 얻을 같은 불운 방식으로 인터리브 수

Thread1: writes dword 1 of value 1 
Thread2: writes dword 1 of value 2 
Thread2: writes dword 2 of value 2 
Thread1: writes dword 2 of value 1 

Result: dword 1 is for value 2, while dword 2 is for value 1 
+3

나는 Interlocked의 영향을 DateTime에 고려해 보았고 몇 가지 유용한 수치를 생각해 냈습니다. 1 초는 1 천만 틱이고 32 비트 경계는 429.5 초 또는 7.166 분입니다. 이는 연동되지 않은 쓰기로 인해 7 분이 지나치게 낮거나 너무 높거나 7 분마다 타임 스탬프가 생성 될 수 있음을 의미합니다. 인터 로킹되지 않은 읽기가있는 인터 로크 된 쓰기는 처음 절반이 먼저 쓰여지기 때문에 타임 스탬프가 7 분으로 너무 높습니다. 즉, 값은 00019999이고, 스레드 1은 00020000을 쓰고 스레드 2는 0002999를보고 스레드 1은 00020000으로 끝납니다. –

+0

@ 크리스 니스! 7 분 오류가 허용되는 사람들을 도울 수 있고 "시간상으로 점프"하는 타임 스탬프가 용인되는 것을 볼 수 있습니다. –

+0

기본 객체 구현과 같이 강력한 신원이없는 객체를 잠그는 것은 좋지 않은 생각입니까? –

0

내 접근이 최고 중 하나가 아닌,하지만 당신은 그냥 날짜 시간으로 구문 분석,이 값을 사용해야하는 경우

class x 
{ 
    string _lastHit; 

    void Touch() 
    { 
    Interlocked.Exchange(ref _lastHit, DateTime.Now.ToString("format your date")); 
    } 
} 

가 :

DateTime.Parse(_lastHit) 

구문 분석은 항상 당신은 형식의 날짜를 저장 한 후 날짜로 다시 구문 분석 문자열 VAR을 사용할 수 있습니다 일, 문자열이 DateTime 클래스를 사용하여 포맷되었으므로 TryParse을 사용하여 가능한 구문 분석 오류를 처리 할 수 ​​있습니다.