I need to run hundreds thousands of functions at a predefined time, in an efficient way,
The code i currently have is like this:
class myclass
{
public DateTime NextTime = DateTime.Now;
Random rand = new Random();
public void DoStuff()
{
if (NeedToWork())
{
// do some complex stuff on a 2nd thread.
NextTime = DateTime.Now.AddSeconds(rand.Next(60, 3600));
}
}
public bool NeedToWork()
{
return DateTime.Now > NextTime;
}
}
the calling function that is run from a timer:
static List<myclass> mylist = new List<myclass>();
static void Activator()
{
foreach (var item in mylist)
{
item.DoStuff();
}
}
My problem is when there is allot of items in the collection, going through all of them takes a very long time, resulting in some DoStuff() functions running late by over a minute in some cases.
Currently the “Activator” function is called from different threads concurrently to make the delay time as low as possible, (necessary thread synchronization is taken care of by using a Mutex)
The 2 solutions I thought of:
- instead of having one
List<myclass>, I could have a dictionary likeDictionary<DateTime, List<myclass>>, with 1 second precision, and the each second, run the appropriate class objects, the dictionay would be mapping ‘nexttime’ to ‘myclass’ instances. - create two
List<>s orQueue<>s instead of the one list, they would be named ‘fastqueue’, and ‘slowqueue’ , slowqueue would have all the objects, fastqueue would have all the items that are soon to need work, and then have a dedicated thread looping through slow queue, and check the remaining time and put it in fastqueue.
Notes:
-
The real code doesn’t have any random data determining the next run time, it is actually based on some calculation, this is only a sample.
-
Each one item doesn’t need more than a fraction of a second to run, and each one run only a maximum of 4 times in a single hour. ram and cpu power is not an issue, I have tested and made allot of optimizations in different areas to make it fit, although not all of code is displayed here.
- The only thing that is wasting cpu time is the line that says return DateTime.Now > NextTime
You might want to maintain a sorted queue or tree of work, sorted by the time to run.
Then your regular interval timer loop runs the first N items from the queue, stopping when an item is beyond the current interval time, as there is no need to look further.
When generating new work to add to the queue, using a sorted data structure, insertion should place it properly to keep it sorted.
(You’ll also need to worry about thread safety in adding and removing from the queue.)
(FYI, There are lots of variations on scheduling algorithms.)
As another note, though not sure if it is useful for you, but when using these kind of time-based formulas, you might consider taking a snapshot of DateTime.Now to use several times, otherwise you can “loose” time if this thread is interrupted between calls to DateTime.Now