gpt4 book ai didi

c# - 简单的无锁秒表

转载 作者:太空宇宙 更新时间:2023-11-03 12:44:33 27 4
gpt4 key购买 nike

根据 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 的不同之处在于,此类基本上一直在运行,所以我不需要保持任何附加状态(“运行”或“停止”),如 秒表 可以。

(更新)

在@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;
}
}
}

澄清一下,这段代码的想法是:

  1. 有一种简单快速的方法来检查自某个操作/事件以来时间是否已经过去,
  2. 如果从多个线程调用,方法不应破坏状态,
  3. 必须对操作系统时钟变化不敏感(用户变化、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(ref _lastResetTime, _stopwatch.ElapsedTicks); 而不是 Milliseconds 因为如果您处于高性能模式,则可以从查询性能计数器

关于c# - 简单的无锁秒表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37799650/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com