System.Threading.Timer
is extremely lightweight. You could create a separate timer for each task so that the timer comes due when the task is supposed to execute.
Internally, I believe that the system will keep track of what timer is due next so it only has to monitor one timer at any given time (source/similar question).