C# 关闭延迟定时器

C# 关闭延迟定时器,c#,multithreading,timer,race-condition,C#,Multithreading,Timer,Race Condition,有没有一种方法可以使用一个System.Threading.timer对象在C#中编写一个安全的1秒延时计时器类 或者,假设输入的打开和关闭速度比每秒一次快得多,那么通常最简单的解决方案是什么 关闭延迟计时器可通过此接口进行描述: public interface IOffDelay { /// <summary> /// May be set to any value from any thread. /// </summary> boo

有没有一种方法可以使用一个System.Threading.timer对象在C#中编写一个安全的1秒延时计时器类

或者,假设输入的打开和关闭速度比每秒一次快得多,那么通常最简单的解决方案是什么

关闭延迟计时器可通过此接口进行描述:

public interface IOffDelay
{
    /// <summary>
    /// May be set to any value from any thread.
    /// </summary>
    bool Input { get; set; }

    /// <summary>
    /// Whenever Input is true, Output is also true.
    /// The Output is only false at startup
    /// or after the Input has been continuously off for at least 1 second.
    /// </summary>
    bool Output { get; }
}
我可以看出此解决方案中存在竞争条件:

  • 在1秒关闭周期结束时,计时器会安排回调运行
  • 有人快速设置和重置输入,重新启动计时器
  • 回调现在终于运行了,检查输入并将输出设置为false,即使它在下一秒应该为true
  • 编辑 彼得·杜尼霍提供了正确的答案,但事实证明我不善于提出正确的问题。当输出变为false时,OffDelay类还应该执行一些操作。以下是根据Peter的基本原则修改的代码:

    public sealed class OffDelay : IOffDelay, IDisposable
    {
        public OffDelay()
        {
            timer = new Timer(TimerCallback, null, Timeout.Infinite, Timeout.Infinite);
        }
    
        public void Dispose()
        {
            timer.Dispose();
        }
    
        public bool Input
        {
            get
            {
                lock (locker)
                    return _input;
            }
            set
            {
                lock (locker)
                {
                    if (value == _input)
                        return;
    
                    _input = value;
    
                    if (_input == true)
                        _output = true;
                    else
                    {
                        stopwatch.Restart();
                        if (!timerRunning)
                            timer.Change(1000, Timeout.Infinite);
                    }
                }
            }
        }
        private bool _input;
    
        public bool Output
        {
            get
            {
                lock (locker)
                    return _output;
            }
        }
        private bool _output;
    
        private readonly object locker = new object();
        private readonly Timer timer;
        private readonly Stopwatch stopwatch = new Stopwatch();
        private bool timerRunning;
    
        private void TimerCallback(object state)
        {
            lock (locker)
            {
                if (_input == true)
                    timerRunning = false;
                else
                {
                    var remainingTimeMs = 1000 - stopwatch.ElapsedMilliseconds;
                    if (remainingTimeMs > 0)
                        timer.Change(remainingTimeMs, Timeout.Infinite);
                    else
                    {
                        _output = false;
                        timerRunning = false;
                        DoSomething();
                    }
                }
            }
        }
    
        private void DoSomething()
        {
            // ...
        }
    }
    
    如果没有一个好的答案来清楚地说明这个问题,包括确切地说明上下文是什么以及可能存在哪些约束,那么就不可能确切地知道什么答案适合你

    但根据您在问题中包含的内容,我将更改您的实现,使其不依赖计时器:

    public sealed class OffDelay : IOffDelay
    {
        public bool Input
        {
            get { lock (locker) return _input; }
            set
            {
                lock (locker)
                {
                    if (value == _input)
                        return;
    
                    _input = value;
                    _lastInput = DateTime.UtcNow;
                }
            }
        }
        private bool _input;
    
        public bool Output
        {
            get { lock (locker) return (DateTime.UtcNOw - _lastInput).TotalSeconds < 1; }
        }
        private DateTime _lastInput;
    }
    
    公开密封的延迟等级:IOffDelay
    {
    公共布尔输入
    {
    获取{lock(locker)return}
    设置
    {
    锁(储物柜)
    {
    如果(值==\u输入)
    返回;
    _输入=值;
    _lastInput=DateTime.UtcNow;
    }
    }
    }
    私人布尔输入;
    公共布尔输出
    {
    获取{lock(locker)return(DateTime.UtcNOw-_lastInput).TotalSeconds<1;}
    }
    私有日期时间\u lastInput;
    }
    

    请注意,上述内容容易受到计算机时钟变化的影响。如果需要独立于时钟工作,可以将
    DateTime.UtcNow
    替换为
    Stopwatch
    实例,每次更改
    Input
    属性时调用
    Reset()
    ,并使用
    秒表的
    appead
    属性来确定自上次输入以来的时间长度。

    我试图避免在发布的问题中出现不必要的复杂情况,但我想我无意中遗漏了一个关键部分:当输出变为false时,类需要做一些事情。不过,你的答案对于所问的问题是正确的,它为我提供了真正解决问题所需的提示。
    public sealed class OffDelay : IOffDelay
    {
        public bool Input
        {
            get { lock (locker) return _input; }
            set
            {
                lock (locker)
                {
                    if (value == _input)
                        return;
    
                    _input = value;
                    _lastInput = DateTime.UtcNow;
                }
            }
        }
        private bool _input;
    
        public bool Output
        {
            get { lock (locker) return (DateTime.UtcNOw - _lastInput).TotalSeconds < 1; }
        }
        private DateTime _lastInput;
    }