简体   繁体   中英

How does a C# Timer Object decide when an elapsed amount of time has occurred?

How does a C# Timer Object decide when an elapsed amount of time has occurred?

I am wondering if it simply loops or is more intelligent than this.

Also, it would be good to know where to find the C# Source Code to have a look through.

The CLR creates a dedicated thread that handles all timer objects you create in your application and gets their Elapsed event and callback handler running. You can see it being used with the debugger. Start with a console mode app 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, Debug tab, tick the "Enable native code debugging" (aka unmanaged code) option. Tools + Options, Debugging, Symbols and ensure that the Microsoft Symbol Server is enabled. Start debugging by pressing F11.

Now use Debug + Windows + Threads. You'll see 4 threads listed. Your main thread, the finalizer thread, the debugging thread and an idle threadpool thread. Keep stepping until you stepped past the t.Start() method call. Note that there's now a new thread added. The name is "ThreadPoolMgr::TimerThreadStart". Double-click it and look at the Call Stack window. You'll 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, that's the start point for the thread that the CLR started. And the SleepEx() call, that's this thread sleeping until the next timer is due. This stack trace was for .NET 4.5, it has been consistent like this all the way back since .NET 2.0. For which source code is available, you can have a look see at the SSCLI20 source code that you can download here . Searching that code takes you to the clr/src/vm/win32threadpool.cpp source code file. Have a look at it to see what is going on.

I'll just briefly describe it. The timing source is the GetTickCount() api function, the same one used by Environment.TickCount. The FireTimers() function sorts out which of the active timers is due first. The SleepEx() function is the same one as Thread.Sleep(), except that it is alertable . It can be interrupted before the sleep is complete by an APC (asynchronous procedure call). Which you see being used in that same file, QueueUserAPC() function when the thread needs to terminate because the program is shutting down and when a timer is added or modified so that a new sleep needs to be calculated.

The .NET BCL has at least two classes providing timers:

However, the second class is implemented using the first. The first class uses some kind of native Win32 timer and Hans Passant has provided more information about the mechanism used. In the BCL source code the timer is implemented using an internal class named TimerQueue . The implementation of the TimerQueue class has this comment:

TimerQueue maintains a list of active timers in this AppDomain. We use a single native timer, supplied by the VM, to schedule all managed timers in the AppDomain.

So basically the AppDomain has a single time source implemented using unmanaged code. See Hans Passant's answer for more information on that.


Also, it would be good to know where to find the C# Source Code to have a look through.

I use JetBrains dotPeek . It can be configured to retrieve source files from Microsoft if they exist. Simply load in the version of .NET you want to examine, hit Ctrl + T , type "Timer", select the specific class you want and it will download the source if it exists. Otherwise you will see a decompiled version (depending on your configuration).

Note that this tool will only allow you to decomple managed code and to fully understand the implementation of timers in .NET you need to be able to peek further into unmanaged code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM