C# &引用;原子地;更改System.Threading.Timer

C# &引用;原子地;更改System.Threading.Timer,c#,.net,C#,.net,假设我有一个现有的System.Threading.Timer实例,我想对其调用Change以将其触发时间向后推: var timer = new Timer(DelayCallback, null, 10000, Timeout.Infinite); // ... (sometime later but before DelayCallback has executed) timer.Change(20000, Timeout.Infinite); 我使用这个计时器在一段时间没有活动后执行“

假设我有一个现有的System.Threading.Timer实例,我想对其调用Change以将其触发时间向后推:

var timer = new Timer(DelayCallback, null, 10000, Timeout.Infinite);
// ... (sometime later but before DelayCallback has executed)
timer.Change(20000, Timeout.Infinite);
我使用这个计时器在一段时间没有活动后执行“空闲回调”。(“空闲”和“无活动”在本例中是应用程序定义的条件……具体情况并不十分重要。)每次执行“操作”时,我都要重置计时器,使其始终设置为在10秒后启动

然而,有一个固有的竞争条件,因为当我调用Change时,我无法判断计时器是否已经根据其旧设置触发。(当然,我可以告诉您是否发生了回调,但我无法告诉CLR的内部计时器线程是否已将我的回调排入线程池队列,并且回调即将执行。)

现在我知道我可以调用timer实例上的Dispose,并在每次需要“向后推”时重新创建它。但这似乎不如仅仅改变现有的计时器有效。当然可能不是……我稍后会运行一些微基准测试,让大家都知道

或者,我可以始终跟踪预期的触发时间(通过DateTime.Now.AddSeconds(10)),如果原始计时器触发,则通过在回调中检查DateTime.Now来忽略它。(我一直担心这可能不是100%可靠,因为计时器使用TimeSpan,我的支票使用DateTime…这可能不是问题,但出于某种原因,我对它不太满意…)

我的问题是:

  • 有没有一个好方法可以让我调用Timer.Change,并且能够知道在回调被排入线程池队列之前我是否成功地更改了它?(我不这么认为,但问一下也无妨……)
  • 还有谁实现过(我称之为)这样的“后推计时器”吗?如果是这样,我很想听听你是如何解决这个问题的
  • 这个问题本质上有点假设性,因为我已经有了两个可行的解决方案(基于Dispose和基于DateTime.Now)…我主要感兴趣的是听取与性能相关的建议(因为我将非常频繁地“向后推”计时器)


    谢谢

    实际上,我不得不为自己制作的MMORPG构建自己的“计时”类。它可以跟踪超过100000个“实体”,这些实体拥有处理人工智能和其他任务的计时器。基于可能采取的不同行动,我将不得不暂时推迟事件

    现在,我的计时课程完全是手工编写的,所以它不是你想要的。但你可以做一些类似于我提出的解决方案的事情,就是做一种:

    while (sleepyTime > 0)
    {
        int temp = sleepyTime;
        sleepyTime = 0;
        Thread.Sleep(temp);
    }
    
    // here's where your actual code is.
    

    然后,您可以创建一个“延迟”方法,基本上只向SleepTime发送广告。

    听起来您真正想要的是应用程序空闲事件

    System.Windows.Forms.Application.Idle
    

    我将您的问题解释为要求实现下面指定的IdleNotifier接口。您还声明ActionOccursed()需要快速

    public delegate void IdleCallback();
    public interface IdleNotifier
    {
        // Called by threadpool when more than IdleTimeSpanBeforeCallback 
        // has passed since last call on ActionOccured.
        IdleCallback Callback { set; }
        TimeSpan IdleTimeSpanBeforeCallback { set; }
        void ActionOccured();
    }
    
    我在下面提供了System.Threading.Timer的实现。 关于实施的要点:

    public class IdleNotifierTimerImplementation : IdleNotifier
    {
        private readonly object SyncRoot = new object();
        private readonly Timer m_Timer;
    
        private IdleCallback m_IdleCallback = null;
        private TimeSpan m_IdleTimeSpanBeforeEvent = TimeSpan.Zero;
    
        // Null means there has been no action since last idle notification.
        private DateTime? m_LastActionTime = null;
    
        public IdleNotifierTimerImplementation()
        {
            m_Timer = new Timer(OnTimer);
        }
    
        private void OnTimer(object unusedState)
        {
            lock (SyncRoot)
            {
                if (m_LastActionTime == null)
                {
                    m_Timer.Change(m_IdleTimeSpanBeforeEvent, TimeSpan.Zero);
                    return;
                }
                TimeSpan timeSinceLastUpdate = DateTime.UtcNow - m_LastActionTime.Value;
                if (timeSinceLastUpdate > TimeSpan.Zero)
                {
                    // We are no idle yet.
                    m_Timer.Change(timeSinceLastUpdate, TimeSpan.Zero);
                    return;
                }
                m_LastActionTime = null;
                m_Timer.Change(m_IdleTimeSpanBeforeEvent, TimeSpan.Zero);
            }
            if (m_IdleCallback != null)
            {
                m_IdleCallback();
            }
        }
    
        // IdleNotifier implementation below
    
        public void ActionOccured()
        {
            lock (SyncRoot)
            {
                m_LastActionTime = DateTime.UtcNow;
            }
        }
    
        public IdleCallback Callback
        {
            set
            {
                lock (SyncRoot)
                {
                    m_IdleCallback = value;
                }
            }
        }
    
        public TimeSpan IdleTimeSpanBeforeCallback
        {
            set
            {
                lock (SyncRoot)
                {
                    m_IdleTimeSpanBeforeEvent = value;
                    // Run OnTimer immediately
                    m_Timer.Change(TimeSpan.Zero, TimeSpan.Zero);
                }
            }
        }
    }
    
    • 我们接受计时器可以在任何时候唤醒,并确保这是正常的
    • 因为我们假设计时器很少唤醒,所以我们可以在这些时候做昂贵的工作
    • 因为我们可以在计时器回调中执行所有逻辑,所以“推计时器”所需要做的就是记住上次推计时器的时间
    实施:

    public class IdleNotifierTimerImplementation : IdleNotifier
    {
        private readonly object SyncRoot = new object();
        private readonly Timer m_Timer;
    
        private IdleCallback m_IdleCallback = null;
        private TimeSpan m_IdleTimeSpanBeforeEvent = TimeSpan.Zero;
    
        // Null means there has been no action since last idle notification.
        private DateTime? m_LastActionTime = null;
    
        public IdleNotifierTimerImplementation()
        {
            m_Timer = new Timer(OnTimer);
        }
    
        private void OnTimer(object unusedState)
        {
            lock (SyncRoot)
            {
                if (m_LastActionTime == null)
                {
                    m_Timer.Change(m_IdleTimeSpanBeforeEvent, TimeSpan.Zero);
                    return;
                }
                TimeSpan timeSinceLastUpdate = DateTime.UtcNow - m_LastActionTime.Value;
                if (timeSinceLastUpdate > TimeSpan.Zero)
                {
                    // We are no idle yet.
                    m_Timer.Change(timeSinceLastUpdate, TimeSpan.Zero);
                    return;
                }
                m_LastActionTime = null;
                m_Timer.Change(m_IdleTimeSpanBeforeEvent, TimeSpan.Zero);
            }
            if (m_IdleCallback != null)
            {
                m_IdleCallback();
            }
        }
    
        // IdleNotifier implementation below
    
        public void ActionOccured()
        {
            lock (SyncRoot)
            {
                m_LastActionTime = DateTime.UtcNow;
            }
        }
    
        public IdleCallback Callback
        {
            set
            {
                lock (SyncRoot)
                {
                    m_IdleCallback = value;
                }
            }
        }
    
        public TimeSpan IdleTimeSpanBeforeCallback
        {
            set
            {
                lock (SyncRoot)
                {
                    m_IdleTimeSpanBeforeEvent = value;
                    // Run OnTimer immediately
                    m_Timer.Change(TimeSpan.Zero, TimeSpan.Zero);
                }
            }
        }
    }
    
    这段代码有许多直接的性能改进

    如果有人对我的第一个想法感兴趣,就问我