The CLR creates a dedicated thread that processes all the timer objects that you create in your application and fires a past event handler and a callback handler. You can see that it is used with a debugger. Start with an application in console mode that looks like this:
using System; using System.Timers; class Program { static void Main(string[] args) { var t = Timer(); t.Elapsed += ElapsedEventHandler((s, e) => { }); t.Start(); } }
Project + Properties, tab "Debugging", check the option "Enable custom code debugging" (the so-called unmanaged code). Tools + Options, Debugging, Symbols and make sure Symbol Server is turned on. Start debugging by pressing F11.
Now use Debug + Windows + Threads. You will see 4 threads. Your main thread, finalizer thread, debug thread, and downstream thread. Continue the step until you complete the call to the t.Start () method. Note that a new stream has now been added. The name is "ThreadPoolMgr :: TimerThreadStart". Double-click it and look at the Call Stack window. You will see:
ntdll.dll!_NtDelayExecution@8 () + 0x15 bytes ntdll.dll!_NtDelayExecution@8 () + 0x15 bytes KernelBase.dll!_SleepEx@8 () + 0x39 bytes clr.dll!ThreadpoolMgr::TimerThreadFire() + 0x3e bytes clr.dll!ThreadpoolMgr::TimerThreadStart() + 0x6a bytes kernel32.dll!@BaseThreadInitThunk @12() + 0x12 bytes ntdll.dll!___RtlUserThreadStart@8 () + 0x27 bytes ntdll.dll!__RtlUserThreadStart@8 () + 0x1b bytes
The important bits here are the TimerThreadStart () function, which is the starting point for the thread that the CLR started. And call SleepEx () so that this thread sleeps until the next timer. This stack trace was for .NET 4.5; it was the same as with .NET 2.0. For what source code is available, you can look in the source code SSCLI20, which you can download here . Searching for this code will lead you to the clr / src / vm / win32threadpool.cpp source file. Look at this to see what happens.
I'll just talk about it. The synchronization source is the GetTickCount () api function, the same as the one used by Environment.TickCount. The FireTimers () function sorts which of the active timers should be first. The SleepEx () function is similar to the Thread.Sleep () function, except that it is a warning function. It can be interrupted before sleep is completed using APC (asynchronous procedure call). You see that the QueueUserAPC () function is used in the same file when the thread must end because the program is shutting down and when the timer is added or changed, so you need to calculate a new dream.
source share