Conditional periodic timer using wait handles

2019-08-06 11:31发布

问题:

I need a timer equivalent which will periodically execute some specific actions (e.g. updating some progress in the database or checking for new Jobs to execute in a database).

These actions are bound to a WaitHandle which specifies if the job needs to be executed or not. So basically this could be, for example, an AutoResetEvent which is set from outside when there is a new Entity in a database and trigger the search for these new Entities. The timer is necessary because I want to limit the number of queries to the database. So if 10 new notifications arrive, the AutomaticResetEvent will only be set one time and there will be also only one query for all of these.

My first attempts looks like this:

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);
    }

The problem with this approach is that WaitHandle.WaitAny will only give me the Index of the first WaitHandle that is triggered. So if I have 2 or more WaitHandles which are triggered, then I will only execute the first actions and not the other ones.

Do you have a better design to achieve the required results of executing all actions which were triggered within the specified time period? If it simplifies the matter I could use another kind of notification mechanism instead of WaitHandles.

Thanks!

回答1:

Maybe you could go with a two thread queue / bulk execution? Note that the code down is just a concept I'm writing without visual studio so I'm doing on a list, this had better to be done with a Queue but I don't remember the syntax without intellisense :) ...

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:

Have you considered using of the the built in Timer classes? System.Threading.Timer is a light-weight implementation that uses the ThreadPool, or System.Timers.Timer which provides related events and more functionality.



回答3:

You'll probably save yourself a lot of hassle by using Reactive Extensions (Rx) - it has lots of great ways to compose events in the sort of manner you're requesting.

This site is a handy way to see the sort of things rx can do.