2016-06-13 6 views
2

MSDN에 따르면 Stopwatch 클래스 인스턴스 메서드는 다중 스레드 액세스에 안전하지 않습니다. 이것은 또한 개별 방법을 검사하여 확인할 수 있습니다. _stopwatch.ElapsedMilliseconds 이후간단한 잠금없는 스톱워치

public class ElapsedTimer : IElapsedTimer 
{ 
    /// Shared (static) stopwatch instance. 
    static readonly Stopwatch _stopwatch = Stopwatch.StartNew(); 

    /// Stopwatch offset captured at last call to Reset 
    long _lastResetTime; 

    /// Each instance is immediately reset when created 
    public ElapsedTimer() 
    { 
     Reset(); 
    } 

    /// Resets this instance. 
    public void Reset() 
    { 
     Interlocked.Exchange(ref _lastResetTime, _stopwatch.ElapsedMilliseconds); 
    } 

    /// Seconds elapsed since last reset. 
    public double SecondsElapsed 
    { 
     get 
     { 
      var resetTime = Interlocked.Read(ref _lastResetTime); 
      return (_stopwatch.ElapsedMilliseconds - resetTime)/1000.0; 
     } 
    } 
} 

는 기본적으로이다 : 만 필요하기 때문에

그러나, 간단한 여전히 같은 것을 사용하여 잠금없는 일을 할 수 있다면 내 코드의 여러 장소에서 타이머, 궁금 "시간 경과" QueryPerformanceCounter을 호출하면 여러 스레드에서 호출하는 것이 안전하다고 생각합니까? 일반 Stopwatch과의 차이점은이 클래스는 기본적으로 항상 실행 중이므로 Stopwatch처럼 추가 상태 ("실행 중"또는 "중지됨")를 유지할 필요가 없다는 것입니다.

(업데이트) 아래 질문에 대해 답 @Scott에 의해 제안 후, 나는 Stopwatch 원시 QueryPerformanceCounter 틱을 반환하는 간단한 정적 GetTimestamp 방법을 제공하는 것을 깨달았다. 이다,

public class ElapsedTimer : IElapsedTimer 
{ 
    static double Frequency = (double)Stopwatch.Frequency; 

    /// Stopwatch offset for last reset 
    long _lastResetTime; 

    public ElapsedTimer() 
    { 
     Reset(); 
    } 

    /// Resets this instance. 
    public void Reset() 
    { 
     // must keep in mind that GetTimestamp ticks are NOT DateTime ticks 
     // (i.e. they must be divided by Stopwatch.Frequency to get seconds, 
     // and Stopwatch.Frequency is hw dependent) 
     Interlocked.Exchange(ref _lastResetTime, Stopwatch.GetTimestamp()); 
    } 

    /// Seconds elapsed since last reset 
    public double SecondsElapsed 
    { 
     get 
     { 
      var resetTime = Interlocked.Read(ref _lastResetTime); 
      return (Stopwatch.GetTimestamp() - resetTime)/Frequency; 
     } 
    } 
} 

이 코드의 아이디어, 명확히하기 :

  1. 검사의 간단하고 빠른 방법을 가지고 즉, 코드는 스레드 안전 인이에 수정 될 수 있습니다 시간이 소정의 운전/이벤트 경과 한 경우
  2. 방법한다 다중 스레드로부터 호출이 아닌 경우 손상된 상태
  3. OS 클럭 변경 (사용자 변경, NTP 동기화, 시간대 등)
  4. 둔감 있어야

나는 비슷한이로 사용합니다 :

private readonly ElapsedTimer _lastCommandReceiveTime = new ElapsedTimer(); 

// can be invoked by multiple threads (usually threadpool) 
void Port_CommandReceived(Cmd command) 
{ 
    _lastCommandReceiveTime.Reset(); 
} 

// also can be run from multiple threads 
void DoStuff() 
{ 
    if (_lastCommandReceiveTime.SecondsElapsed > 10) 
    { 
     // must do something 
    } 
} 
+0

Interlocked.Exchange와 Interlocked.Read는 내가 믿는 잠금 메커니즘입니다. –

+0

@ justin.m.chase : 아니오, 둘 다 잠금이 없습니다 (원자 성을 보장하면서). x64 플랫폼에서는 실제 CPU 명령어로 JITted를 얻습니다. – Lou

+1

지금까지 QueryPerformanceCounter를 직접 호출하지 않는 이유는 무엇입니까? –

답변

1

고성능 모드 인 경우 보조 밀리 초 결과를 QueryPerformanceCounter에서 가져올 수 있기 때문에 밀리 초 대신 Interlocked.Exchange(ref _lastResetTime, _stopwatch.ElapsedTicks);을 사용하는 것이 좋습니다.

+0

+1 감사합니다. 하지만 한 가지 수정 사항 :'Stopwatch' 진드기는 실제로'QueryPerformanceCounter'에 의해 반환 된 원시 진드기이며,'DateTime'에 의해 사용 된 진드기는 100ns가 아닙니다. – Lou

+0

네 말이 맞아, 나는 내 대답을 고칠거야. –

0

내가 Stopwatch의 여러 인스턴스를 생성하고는 동일한 스레드에서 그것을 읽는 추천 할 것입니다.

Stopwatch watch = Stopwatch.Startnew(); 
DoAsyncWork((err, result) => 
{ 
    Console.WriteLine("Time Elapsed:" + (watch.ElapsedMilliseconds/1000.0)); 
    // process results... 
}); 

또는를 :

나는 사이비 코드에서 나도 할 것 같은하지만 비동기 코드가 어떻게 생겼는지 모르는

public DoAsyncWork(callback) // called asynchronously 
{ 
    Stopwatch watch = Stopwatch.Startnew(); 
    // do work 
    var time = watch.ElapsedMilliseconds/1000.0; 
    callback(null, new { time: time }); 
} 

첫 번째 예는 DoAsyncWork 작업이 작업을 수행한다고 가정 그러면 다른 스레드에서 호출자 스레드로 마샬링되어 완료되면 콜백이 호출됩니다.

두 번째 예제는 호출자가 스레딩을 처리 중이며이 함수가 모든 타이밍 자체를 수행하여 결과를 호출자에게 다시 전달한다고 가정합니다.

+0

하지만 요점은 모든 스레드에서 워치 독을 재설정하고 시간이 경과했는지 안전하게 확인할 수 있어야한다는 것입니다. 또한, 첫 번째 스 니펫은'watch' 변수를 캡쳐하지만, 비동기 메소드 밖에서는 액세스하지 못하도록하지 않습니다. 기본적으로 static p/invoke를'QueryPerformanceCounter'에만 필요로하기 때문에'timer.stopwatch'를 사용하고 있습니다. 그래서 타이머의 각 인스턴스마다 모든 필드를 할당 할 필요가 없습니다 (단지'long' 만 필요합니다). . – Lou

+0

그러면 잠금을해야 할 것입니다. –