Scheduling Multiple Tasks Reliably

thedesolatesoul

Expert
Licensed User
I have a list of tasks in my service, each of them contains a time on which they need to be scheduled. Some of them will be scheduled at the same time.
I am wondering if there is a foolproof way to make sure that I do not miss running any of those tasks.

For e.g. if I have 10 tasks in a list.

and on the next schedule of StartServiceAt, I need to schedule 3 of the tasks.
1) How certain is that the Service will start at the exact time (within +/- 1 min)?

I will have to do something like:

B4X:
For i = 0 to Tasks.Size -1
  if Tasks.Time = DateTime.Now then
       DoTask(Task(i))
  End If
Next
I could miss the time since the service is a bit late (due to message queue etc).

2) Secondly, what if I need to start a task WHILE the service is already doing something? So, the service is busy in downloading something, but its time for another Task to start.
 

Erel

B4X founder
Staff member
Licensed User
This code will NOT work properly:
B4X:
For i = 0 to Tasks.Size -1
  if Tasks.Time = DateTime.Now then
       DoTask(Task(i))
  End If
Next
You should instead have a list with all the tasks sorted in chronological order. I recommend you to use a database for that.

Each time that the service is started you should:
- Find all the tasks that were not run yet and their time has passed (or is now), and run them.
- Find the time of the next task and schedule the service with StartServiceAt.

It is possible that the service will be started while it is already running a previous task. Your code should be able to cope with this.
 
Upvote 0

thedesolatesoul

Expert
Licensed User
I'm using a file to write out all tasks that can occur rather than a database for now. I may migrate later.

So lets say Task is a custom type with
B4X:
Type TaskType ( _
LastRunTime as Long, _
NextRunTime as Long)

Now, initially (and at the end of each service run), I will run a scheduler that will set the NextRunTime for each task.
The smallest of these NextRunTime will be used in StartServiceAt("",SmallestNextRunTime)

When the service runs, it checks for which tasks need to be done:
B4X:
For i = 0 to TaskList.Size -1
  if DateTime.Now > Tasks.NextRunTime then
       DoTask(Task(i))
  End If
Next

And then before getting destroyed, it schedules the next task as above.
This sounds okay so far.

It is possible that the service will be started while it is already running a previous task. Your code should be able to cope with this.
So after completing the task, when running the scheduler for the next start, I also need to be checking which Tasks should have been run in this duration but were not run?
For this I will have to see if:
DateTime.Now > NextRunTime
and I will re-start the service straight away.

B4X:
CalculateNextRunTimes
For i = 0 to TaskList.Size -1
  if DateTime.Now > Tasks.NextRunTime then
       'We have tasks pending
       StartService("") 'Will this work with the service already active?
  End If
Next


I'm not sure what happens if a service is already processing code, and it is started again? Will it start from Service_Start again? And then I have to check Service.IsRunning?

Thanks.
 
Upvote 0

thedesolatesoul

Expert
Licensed User
Nothing special happens. Service_Start will be called again (when the main thread is free to process the start message).
That sounds good then. The service runs on the main thread, and checks for Tasks to do only in its dying moments so it will surely be created again.

Just one last question, regarding hc.GetAsynchronously
The GetAsynchronously probably runs in a child thread in the service, thus preventing it from getting destroyed. However does the Service_Create sub complete execution at the point? I'm guessing it does. So I need to put my scheduler code in Service_Destroyed instead.
 
Upvote 0

thedesolatesoul

Expert
Licensed User
The service will not be destroyed automatically when the task is completed. You should not use this sub to schedule the next task.
Yes, I forgot that.
I will put the scheduler where I expect the tasks to end. (In my case at the end of ProcessQue).
But GetAsynchronously does baffle me a bit? When downloading a large file, I expect all Subs to complete processing except for that thread. Thus Service_Start will have completed. And the only code I can execute is after the Http Response. So i will call ProcessNextTask from there, and when there are no more tasks then run the Scheduler.
 
Upvote 0

thedesolatesoul

Expert
Licensed User
Why not schedule the tasks at the beginning?
Will the tasks not run properly if they are executed at the same time?
So each task is downloading/uploading a list of files.
Scheduling the task at the beginning means, that the service has already been invoked and will be busy for awhile. If I schedule the next task that needs to be done at the beginning, and the service has not yet finished the last task before getting another call at StartServiceAt, I do not know what will happen!
You said that Service_Start will be run when the main thread has finished processing, but as the download is happening in a background thread, this means that Service_Start will be run again WHILE the background thread is working. Since I have a queueing mechanism the next task will not start downloading but will wait until its turn.
Come to think of it, it might work! But it will be incredibly difficult for me to debug. I will try this method.
Thanks Erel.
 
Upvote 0
Top