1

I want to achieve the following:

Let's say I have these tasks:

#1 - Executed every 10 seconds.

#2 - Executed every 2h, needs consistent results from #1.

#3 - Executed every day at 10:00 and 22:00, needs consistent results from #1.

#4 - Executed every Sunday at 22:00 and every Friday at 22:00, needs consistent results from #1 and #2.

#5 - Executed every 1st day of the month, but need newest values from #2 and therefore will execute #2 before executing itself.

With consistent results I mean that, for example, #2 cannot start if #1 is running because otherwise #1 may still change some variables and therefore needs to wait for #1 to complete.

With regards to #5 I'd like that execution of #2 by #5 won't change #2 scheduled execution, therefore if #5 starts #2 when #2 is five minutes away from its 2h execution, #2 will run again in 5 minutes.

Do you know if such a task scheduler already exists, or if there is a pattern I can implement to achieve the above? Or can you give me some hints?

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Luca
  • 333
  • 2
  • 13
  • Did you read the site rules? The part about "no product recommendations"? Regarding patterns - what you look for is more an enterprise level scheduling application. #1 task gets nasty as it runs overlapping and still somehow should be consistent. 10 seconds are really challenging it in this - looks like someone botched #1 programming. – TomTom Nov 03 '18 at 13:12
  • Sorry for that, I am not looking for a paid solution, but for an open source library (if it exists). About the 10 seconds, it's because what it needs to do is not resource intensive, but none the less it cannot run on the main Interface thread. At the moment I didn't do anything to solve the problem because first of all I wanted to hear ideas from people who know more than me about the argument. – Luca Nov 03 '18 at 13:17
  • Listen, I am not here to argue, but to find solutions. – Luca Nov 03 '18 at 13:36
  • Luca, I apologize for the rudeness that you have encountered here. I have flagged several of these comments for moderators to address. However, product recommendations (free or otherwise) are off-topic here and so your question will still be closed. You will have better luck if you can start with a simple implementation and then ask specific questions as you need help. – BJ Myers Nov 05 '18 at 05:55
  • Thank you very much for your kind reply and suggestion. I'll try a basic implementation and come back to SO. – Luca Nov 05 '18 at 09:04

1 Answers1

0

Let me rephrase the requirements, because the term "consistent results" might be confusing.

#1 - Executed every 10 seconds. Executions should not overlap with #2, #3 or #4.

#2 - Executed every 2 hours. Executions should not overlap with #1, #4 or #5.

#3 - Executed every day at 10:00 and 22:00. Executions should not overlap with #1.

#4 - Executed every Sunday at 22:00 and every Friday at 22:00. Executions should not overlap with #1 or #2.

#5 - Executed every 1st day of the month. Executions should not overlap with #2. Should invoke #2 just before starting.

I think that the requirements can be met by using 5 Mutex objects, one for each task. Then before starting a task you acquire its corresponding Mutex, and all Mutexes of the relevant mutually exclusive tasks, all at once as an atomic operation. After acquiring them, immediately release all the acquired Mutexes except from the one that corresponds to the current task, and then start the task. Finally, when the task completes, release its own Mutex as well.

So for example for the task #1, acquire the Mutexes 1, 2, 3 and 4, then immediately release the Mutexes 2, 3 and 4, and finally release the Mutex 1. This way the task #1 will not start while any of the #2, #3 or #4 are running, and while it's running it will prevent any of the #2, #3 or #4 from starting. Below is a complete implementation of this idea.

private static readonly Mutex _m1 = new();
private static readonly Mutex _m2 = new();
private static readonly Mutex _m3 = new();
private static readonly Mutex _m4 = new();
private static readonly Mutex _m5 = new();

private static void RunSynchronized1()
{
    // Should not overlap with #2, #3 or #4.
    Mutex self = _m1;
    Mutex[] other = new Mutex[] { _m2, _m3, _m4 };
    WaitHandle.WaitAll(other.Prepend(self).ToArray());
    Array.ForEach(other, h => h.ReleaseMutex());
    try { Run1(); } finally { self.ReleaseMutex(); }
}

private static void RunSynchronized2()
{
    // Should not overlap with #1, #4 or #5.
    Mutex self = _m2;
    Mutex[] other = new Mutex[] { _m1, _m4, _m5 };
    WaitHandle.WaitAll(other.Prepend(self).ToArray());
    Array.ForEach(other, h => h.ReleaseMutex());
    try { Run2(); } finally { self.ReleaseMutex(); }
}

private static void RunSynchronized3()
{
    // Should not overlap with #1.
    Mutex self = _m3;
    Mutex[] other = new Mutex[] { _m1 };
    WaitHandle.WaitAll(other.Prepend(self).ToArray());
    Array.ForEach(other, h => h.ReleaseMutex());
    try { Run3(); } finally { self.ReleaseMutex(); }
}

private static void RunSynchronized4()
{
    // Should not overlap with #1 or #2.
    Mutex self = _m4;
    Mutex[] other = new Mutex[] { _m1, _m2 };
    WaitHandle.WaitAll(other.Prepend(self).ToArray());
    Array.ForEach(other, h => h.ReleaseMutex());
    try { Run4(); } finally { self.ReleaseMutex(); }
}

private static void RunSynchronized5()
{
    // Should not overlap with #2. Should invoke #2 just before starting.
    RunSynchronized2();
    Mutex self = _m5;
    Mutex[] other = new Mutex[] { _m2 };
    WaitHandle.WaitAll(other.Prepend(self).ToArray());
    Array.ForEach(other, h => h.ReleaseMutex());
    try { Run5(); } finally { self.ReleaseMutex(); }
}

The 5 tasks are represented by the methods Run1, Run2, Run3, Run4 and Run5, whose definition is omitted. The RunSynchronized1 is the synchronized version of Run1, etc. You can use a timer component like the PeriodicTimer to schedule the RunSynchronized1 every 10 seconds, the RunSynchronized2 every 2 hours, etc (example). For top of the minute scheduling you could use the CronosPeriodicTimer that I have posted as an answer to a relevant question.

The above setup assumes that your program will be running continuously for months. If you don't like this idea, and you would prefer to let the Windows Scheduler start your program for executing just a single task each time, you would have to do some changes. You would need to instantiate the Mutexes with the constructor that has a name parameter (named mutex), giving a unique name to each instance, so that they are effective across process boundaries. You would also need to change the RunSynchronized1 that is invoked frequently, passing the TimeSpan.Zero as second parameter of the WaitHandle.WaitAll, and exiting the program if the result is false. Otherwise you may end up with a ton of instances of your program waiting their turn to execute the Run1 task.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104