【问题标题】:Conditional periodic timer using wait handles使用等待句柄的条件周期性定时器
【发布时间】:2011-05-13 08:48:09
【问题描述】:

我需要一个等效的计时器,它会定期执行一些特定操作(例如,更新数据库中的某些进度或检查要在数据库中执行的新作业)。

这些操作绑定到一个 WaitHandle,它指定作业是否需要执行。所以基本上这可能是,例如,当数据库中有一个新实体并触发对这些新实体的搜索时,从外部设置一个 AutoResetEvent。计时器是必要的,因为我想限制对数据库的查询次数。因此,如果有 10 条新通知到达,则 AutomaticResetEvent 只会设置一次,并且所有这些也只会有一次查询。

我的第一次尝试是这样的:

class ConditionalScheduler
{
    public void AddConditionalAction(WaitHandle handle, Action action)
    {
        lock (syncRoot)
        {
            handles.Add(handle);
            actions.Add(handle, action);
        }
    }

    private readonly object syncRoot = new object();

    private readonly Dictionary<WaitHandle, Action> actions = new Dictionary<WaitHandle, Action>();
    private readonly List<WaitHandle> handles = new List<WaitHandle>();

    public void RunTimerLoop()
    {
        do
        {
            WaitHandle[] waitHandles = handles.ToArray();
            int index = WaitHandle.WaitAny(waitHandles);

            if (index >= 0)
            {
                WaitHandle handle = waitHandles[index];

                Action action;
                lock (syncRoot)
                {
                    action = actions[handle];
                }

                action();
            }
            Thread.Sleep(5000);
        } while (true);
    }

这种方法的问题是 WaitHandle.WaitAny 只会给我触发的第一个 WaitHandle 的索引。因此,如果我有 2 个或更多 WaitHandles 被触发,那么我将只执行第一个操作,而不执行其他操作。

您是否有更好的设计来实现执行在指定时间段内触发的所有操作所需的结果?如果它可以简化问题,我可以使用另一种通知机制而不是 WaitHandles。

谢谢!

【问题讨论】:

    标签: c# .net timer synchronization waithandle


    【解决方案1】:

    也许您可以使用两个线程队列/批量执行? 请注意,下面的代码只是我在没有 Visual Studio 的情况下编写的一个概念,所以我在一个列表中进行操作,最好使用 Queue 来完成,但我不记得没有智能感知的语法 :) ...

    private readonly Dictionary<WaitHandle, Action> actions = new Dictionary<WaitHandle, Action>();
        private readonly List<WaitHandle> handles = new List<WaitHandle>();
        private readonly List<WaitHandle> triggeredHandles = new List<WaitHandle>();
    
    // thread 1
        public void CheckLoop()
        {
            do
            {
                WaitHandle[] waitHandles = handles.ToArray();
                int index = WaitHandle.WaitAny(waitHandles);
    
                if (index >= 0)
                {
                    lock (triggeredHandles)
                    {
                       triggeredHandles.Add(waitHandles[index]);
                    }
    
                }            
            } while (true);
        }
    
    // thread 2
    public void RunLoop()
        {
            do
            {
                lock(triggeredHandles)
                {
                    foreach (var th in triggeredHandles) {
    
                       Action action;
                       lock (syncRoot)
                       {
                           action = actions[th];
                       }
    
                       action();                   
                    }
                    triggeredHandles.Clear();
                }
                Thread.Sleep(5000);
            } while (true);
        }
    

    【讨论】:

    • 这看起来可行。但我希望有一个只涉及一个线程的解决方案......这对我来说使用 2 个线程似乎有点沉重。
    • 打开 Windows 任务管理器并转到性能选项卡,查看您的操作系统上正在运行的线程数。我现在有832。所以我认为一个moe不会那么糟糕:D
    • 顺便说一句,这可以在一个线程中完成:如果将句柄添加到队列中,然后不等待 5000,如果要执行的句柄超过 X,则全部执行,然后等待 5000跨度>
    • 我只使用一个线程解决了它:在第一个 WaitAny 之后,我在所有剩余的等待句柄上调用 WaitOne,超时设置为 0。这将立即返回,但如果 WaitHandle 被触发,仍会返回我.不确定这是否是您的意思,但我认为它会起作用。无论如何我都会接受这个作为答案,谢谢!
    【解决方案2】:

    您是否考虑过使用内置的 Timer 类? System.Threading.Timer 是一个轻量级实现,它使用 ThreadPoolSystem.Timers.Timer 提供相关事件和更多功能。

    【讨论】:

    • 这并不能真正解决我的问题。我知道如何定期执行操作。我不知道的是如何引入条件行为......
    • @aKzenT:当然可以,但仍然值得注意的是Thread.Sleep 存在一些问题:它在时间意义上不准确,它保留了一个线程但不让它做任何工作,并且不能被打断。
    • +1,你是对的,虽然在我的情况下准确性并不那么重要......我不喜欢这些计时器类是它们在线程池上运行。我想让“滴答声”总是在同一个线程上。不知道如何使用计时器类来解决这个问题。
    【解决方案3】:

    使用Reactive Extensions (Rx) 可能会为您省去很多麻烦 - 它有很多很棒的方法可以按照您要求的方式组织事件。

    This site 是查看 rx 可以做什么的便捷方式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多