根據MSDN,Stopwatch
類實例方法對於多線程訪問不安全。這也可以通過檢查個別方法加以確認。簡單的無鎖秒錶
然而,因爲我只需要簡單的「時間流逝」在我的代碼的幾個地方定時器,我想知道,如果它仍然可以使用像做無鎖,:
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;
}
}
}
由於_stopwatch.ElapsedMilliseconds
基本上是一個致電QueryPerformanceCounter
,我假設從多線程調用是安全的?與常規Stopwatch
的區別在於,此類基本上一直在運行,所以我不需要保留任何其他狀態(「運行」或「停止」),就像Stopwatch
一樣。
(更新)
通過@Scott在下面的答案提出的建議之後,我意識到Stopwatch
提供一個簡單的靜態GetTimestamp
方法,該方法返回原QueryPerformanceCounter
蜱。換句話說,代碼可以被修改,以這一點,這是線程安全的:
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;
}
}
}
這段代碼的思想,澄清是:
- 有檢查的一種簡單,快捷的方式如果由於某種操作/事件時間已經過去時,
- 方法應該如果從多個線程調用時,未損壞狀態
- 必須是不敏感的OS時鐘變化(用戶改變,NTP同步,時區等)
我會用它與此類似:
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
}
}
'Interlocked.Exchange'和'Interlocked.Read'是一個鎖定機制,我相信 –
@ justin.m.chase:不,它們都是無鎖的(同時確保原子性)。在x64平臺上,他們甚至會根據實際的CPU指令進行打印。 – Lou
如果你要這麼做,爲什麼不自己調用QueryPerformanceCounter? –