Reputation: 143
I want to execute methods at different intervals and was looking at using the Timer class to schedule this. However, I wanted to understand if the Timer spun up a new thread for each new schedule which could then potentially impact performance of the application
Upvotes: 0
Views: 181
Reputation: 117175
Dai's done a good job of answering the question regarding timers and threads.
I thought I'd give you an alternative way of writing your code. You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive
and add using System.Reactive.Linq;
- then you can do this:
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
EventLoopScheduler els = new EventLoopScheduler();
els.Schedule(() => Console.WriteLine(Thread.CurrentThread.ManagedThreadId));
IObservable<string> pings = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(30), els).Select(x => "Ping!");
IObservable<string> pongs = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(20), els).Select(x => "Pong!");
IObservable<string> pangs = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(10), els).Select(x => "Pang!");
IObservable<string> query = Observable.Merge(els, pings, pongs, pangs);
IDisposable subscription = query.Subscribe(x => Console.WriteLine($"{x} ({Thread.CurrentThread.ManagedThreadId})"));
Console.ReadLine();
subscription.Dispose();
els.Dispose();
The EventLoopScheduler
creates a single, dedicated, re-usable thread that you can used until you call .Dispose()
on it.
Both Observable.Timer
and Observable.Merge
allow you to specify that you want to use the EventLoopScheduler
to ensure that the code is run on that thread.
Upvotes: 2
Reputation: 155658
System.Threading.Timer
:Short-answer: 90% of the time: no. It uses the Thread Pool to get an existing thread (that isn't doing anything).
Long-answer: Possibly! If all threads in the pool are busy then a new thread will need to be created by the OS and added to the pool and then used by the Timer.
https://learn.microsoft.com/en-us/dotnet/api/system.threading.timer?view=netframework-4.8
Provides a mechanism for executing a method on a thread pool thread at specified intervals. This class cannot be inherited.
System.Windows.Forms.Timer
triggers a new Win32 Window Message to be sent to the Form
at a set interval. It does not use a dedicated thread nor a pool thread, instead it uses Win32's SetTimer
function.
System.Timers.Timer
will also use a pool thread by default (just like System.Threading.Timer
) but lets you perform thread synchronization. See https://learn.microsoft.com/en-us/dotnet/api/system.timers.timer?view=netframework-4.8
I recommend you look at using await Task.Delay
instead - as it won't cause a new thread to be used (remember that Task
s are not Threads) - though if you do use Task.Run
to run the coroutine in a pool thread then it may run on a new thread:
public static class Foo
{
public static async Task<Int32> Main( String[] args )
{
Task loop30 = this.Every30Seconds();
Task loop20 = this.Every20Seconds();
Taks loop10 = this.Every10Seconds();
await Task.WhenAll( loop30, loop20, loop10 );
return 0;
}
public static async Task Every30Seconds()
{
while( true )
{
Console.WriteLine("Ping!");
await Task.Delay( 30 * 1000 );
}
}
public static async Task Every20Seconds()
{
while( true )
{
Console.WriteLine("Pong!");
await Task.Delay( 20 * 1000 );
}
}
public static async Task Every10Seconds()
{
while( true )
{
Console.WriteLine("Pang!");
await Task.Delay( 10 * 1000 );
}
}
}
Upvotes: 4