If you want to limit the number of threads then you only need to think about the scheduler you use, this has nothing to do with the number of timers created. Time-based operations schedule actions with due times and it's the scheduler that decides how to dispatch events at the correct time - Rx is smart about how many timers actually get created and this mechanism is dependent on the scheduler used.
The default scheduler on most platforms used by time-based operators (Scheduler.Default
) will use the Task pool to obtain the thread to dispatch an event that was scheduled at a future time and this is why you will generally see different threads being used to dispatch events.
One way to control threads is to use an EventLoopScheduler
and ensure you specify this when using time-based operations. This will dispatch all it's events on the same thread. For example:
var scheduler = new EventLoopScheduler();
Observable.Return(1)
.Delay(TimeSpan.FromSeconds(4), scheduler)
.Subscribe(x => Console.WriteLine(x.ToString() + ": "
+ Thread.CurrentThread.ManagedThreadId));
Observable.Return(2).Delay(TimeSpan.FromSeconds(2), scheduler)
.Subscribe(x => Console.WriteLine(x.ToString() + ": "
+ Thread.CurrentThread.ManagedThreadId));
Console.WriteLine("Done.");
Will output something like (ThreadId will vary of course):
Done.
2: 3
1: 3
Whereas if you change the first line to var scheduler = Scheduler.Default;
you would see different thread IDs.
The topic of timers in Rx is quite complex and probably a bit too broad for this format - this excellent post covers a lot of the internal details. See the section a fair way down "It's all about time."