Explain the timer Timer class in C and its garbage collection mechanism in detail

  • 2021-09-24 23:20:57
  • OfStack

About C # Timer class in C # there are three about timer classes

Method 1 used by C # Timer. Defined in System. Windows. Forms

Method 2 used by C # Timer. Defined in class System. Threading. Timer "

Method 3 used by C # Timer. Defined in System. Timers. Timer class

Let's take a detailed look at the explanations of these three usages of C # Timer:

(1) System. Windows. Forms. Timer

Applied in WinForm, it is implemented by Windows message mechanism, similar to Timer control in VB or Delphi, and implemented internally by API SetTimer. Its main drawback is that the timing is imprecise, and there must be a message loop, so Console Application (console application) cannot be used.

(2) System. Timers. Timer

Very similar to System. Threading. Timer, they are implemented through. NET, Thread and Pool, which are lightweight, accurate in timing and have no special requirements for applications and messages.

(3) System. Timers. Timer can also be applied to WinForm, completely replacing the Timer control above. Their disadvantage is that they do not support direct drag-and-drop and need manual coding.

C # Timer Usage Example


 Use System.Timers.Timer Class 

System.Timers.Timer t =  
new System.Timers.Timer(10000); 
// Instantiation Timer Class, set the interval to 10000 Milliseconds;   
t.Elapsed +=  
new System.Timers.ElapsedEventHandler(theout); 
// Execute the event when the arrival time;   
t.AutoReset = true; 
// Setting is to execute 1 Times ( false ) Or 1 Direct execution (true) ;   
t.Enabled = true; 
// Whether to execute System.Timers.Timer.Elapsed Events;   
 
public void theout( 
object source,  
System.Timers.ElapsedEventArgs e)  
 {  
  MessageBox.Show("OK!");  
 } 


Garbage Collection Mechanism of Timer
Usually, when we need to execute a task regularly, we need a timer, and then we can use the Timer timer in c # System. Threading space; It is an asynchronous timer, and when the time comes, it allocates one thread in the thread pool to perform tasks every time. Let's look at an interesting example:


class Program
  {
    static void Main(string[] args)
    {
      Timer timer = new Timer(TimerCallback,null,0,2000);
      
      Console.ReadLine();
    }
 
    private static void TimerCallback(object o)
    {
      Console.WriteLine("in TimerCallback method");
      GC.Collect();
 
      
    }
  }

When we run this program in debug mode, as we expect, the program will execute this method every 2 seconds and print "in TimerCallback method", while when we execute it in release mode, we only execute this method once and print the string once. Here, when we call the TimerCallback method, we force the garbage collector, which shows that in release mode, when the garbage collector executes the recycling algorithm, we first assume that all objects are recyclable. When the Timer object is assigned to the variable t, t is not referenced, so there is no variable referencing Timer object, so garbage collection will recycle Timer object at this time. So why in debug mode can run energy, this is related to the optimization of c # compiler, and the compiler has done related optimization operations in release mode. In debug mode, the generation period of timer object is the end of the method, which is also for the convenience of debugging. Otherwise, during debugging, when we want to see the value of timer after executing Timer timer = new Timer (), it has been collected by the garbage collector. This is an unexpected result. How does the compiler handle it? We can look at the compiler compiling the above code in release mode and debug mode. Compared with our known results.

The release schema compiles the resulting IL:


.method private hidebysig static void Main(string[] args) cil managed
{
 .entrypoint
 // Code size    32 (0x20)
 .maxstack 8
 IL_0000: ldnull
 IL_0001: ldftn   void GCTest.Program::TimerCallback(object)
 IL_0007: newobj   instance void [mscorlib]System.Threading.TimerCallback::.ctor(object,
                                           native int)
 IL_000c: ldnull
 IL_000d: ldc.i4.0
 IL_000e: ldc.i4   0x7d0
 IL_0013: newobj   instance void [mscorlib]System.Threading.Timer::.ctor(class [mscorlib]System.Threading.TimerCallback,
                                       object,
                                       int32,
                                       int32)
 IL_0018: pop
 IL_0019: call    string [mscorlib]System.Console::ReadLine()
 IL_001e: pop
 IL_001f: ret
} // end of method Program::Main

IL generated in debug mode:


method private hidebysig static void Main(string[] args) cil managed
{
 .entrypoint
 // Code size    33 (0x21)
 .maxstack 4
 .locals init ([0] class [mscorlib]System.Threading.Timer timer)
 IL_0000: nop
 IL_0001: ldnull
 IL_0002: ldftn   void GCTest.Program::TimerCallback(object)
 IL_0008: newobj   instance void [mscorlib]System.Threading.TimerCallback::.ctor(object,
                                           native int)
 IL_000d: ldnull
 IL_000e: ldc.i4.0
 IL_000f: ldc.i4   0x7d0
 IL_0014: newobj   instance void [mscorlib]System.Threading.Timer::.ctor(class [mscorlib]System.Threading.TimerCallback,
                                       object,
                                       int32,
                                       int32)
 IL_0019: stloc.0
 IL_001a: call    string [mscorlib]System.Console::ReadLine()
 IL_001f: pop
 IL_0020: ret
} // end of method Program::Main

From the generated IL, we can see that in debug mode, there are 19 lines of IL script in red font generated in IL mode than in release mode. The function of this script is to store the variables on the stack that reference Timer objects generated in 15 lines into local variable 0. Therefore, in debug mode, the t is still referenced, and the Timer object cannot be recycled, so the expected results can also appear, so how can we get the expected results in both modes? We can do the following.

Correct code:


class Program
  {
    static void Main(string[] args)
    {
      Timer timer = new Timer(TimerCallback,null,0,2000);
    
      Console.ReadLine();
      timer.Dispose();
    }

    private static void TimerCallback(object o)
    {
      Console.WriteLine("in TimerCallback method");

      GC.Collect();

      
    }
  }

Our callback method is called every 2 seconds, whether in release mode or debug mode.


Related articles: