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.