根据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? –