English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
★Preface
Opening the long-lost Live Writer, and it's been a long time since I last wrote a blog. I'm really too lazy. No more废话, let's get straight to the theme of this blog--Timer. Why write this? Because a few days ago, at the invitation of a friend, I wanted to make a 'hacker' utility, which is quite simple: automatically get the contents of the clipboard and send emails. This requires using Timer to cyclically get the contents of the clipboard, but when it comes to the function of sending emails, using C#SmtpClientI can't send emails all the time. I have written similar email functions before, which could be used with NetEase at that time, but now it can't be used anymore. I don't know what's going on, so I can only give up. I encountered a problem I hadn't thought of before while using Timer--Reentrant.
★Introduction
First, let's briefly introduce the timer. Here, the timer refers to System.Timers.timer, which, as the name implies, can trigger events at specified intervals. The official introduction is here, excerpted as follows:
The Timer component is a server-based timer that allows you to specify periodic intervals in the application to trigger Elapsed events. Then, you can provide regular processing by handling this event. For example, suppose you have a critical server that must be checked weekly 7 day, every day 24 The hour runs continuously. You can create a service using Timer to regularly check the server and ensure that the system is turned on and running. If the system does not respond, the service can attempt to restart the server or notify the administrator. The server timer is designed for use in a multi-threaded environment to assist threads. The server timer can move between threads to handle the引发的 Elapsed event, making it more precise than the Windows timer in triggering events on time.
If you want to know what the differences are with other timers, you can see here, there is a detailed introduction, no more words (actually I don't know there are so many). So what are the benefits of using this timer? Mainly because it is implemented through .NET Thread Pool, lightweight, timing accurate, and has no special requirements for the application and messages.
★Use
Let's briefly introduce how to use this Timer, it's actually very simple, I just use the example provided by Microsoft for testing, let's directly show the code:
//Do not declare Timer as a local variable, otherwise it will be collected by GC private static System.Timers.Timer aTimer; { //Instantiate the Timer class and set the interval time to10000 milliseconds); aTimer = new System.Timers.Timer(10000); //Register the timer event aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent); //Set the time interval to2seconds (2000 milliseconds), overriding the interval set in the constructor aTimer.Interval = 2000; //Set whether to execute once (false) or continuously (true), the default is true aTimer.AutoReset = true; //Start timing aTimer.Enabled = true; } //Specify the event triggered by Timer private static void OnTimedEvent(object source, ElapsedEventArgs e) { Console.WriteLine("The event triggered occurred at: {0}", e.SignalTime); }
The running result is as follows, the timing is quite accurate:
/* Press any key to exit the program. The event triggered occurred at: 2014/12/26 Friday 23:08:51 The event triggered occurred at: 2014/12/26 Friday 23:08:53 The event triggered occurred at: 2014/12/26 Friday 23:08:55 The event triggered occurred at: 2014/12/26 Friday 23:08:57 The event triggered occurred at: 2014/12/26 Friday 23:08:59 */
★Reentrancy issue reproduction and analysis
What is reentrancy? It is a concept related to multithreading programming: when multiple threads run simultaneously in a program, it is possible for the same method to be called by multiple processes at the same time. When there are some non-thread-safe code in this method, method reentrancy can lead to data inconsistency. Timer reentrancy refers to the use of multithreading timers, where one timer is still processing and another timer will continue to enter the method for processing when the time comes. Below is a demonstration of how reentrancy issues arise (it may not be easily reproducible, but it can still simply illustrate the problem):
//used to cause thread synchronization problems 1; //1 { 10 + } /// <summary> /// The callback method of System.Timers.Timer /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private static void TimersTimerHandler(object sender, EventArgs args) { int t = ++num; Console.WriteLine(string.Format("Thread {0} output: {1}, Output Time: {2 System.Threading.Thread.Sleep(2000); outPut++; Console.WriteLine(string.Format("Thread {0} increment1Post Output: {1}2 }
The following is the output result:
Don't you feel that the output result above is very strange? First, the thread1the output is1There is no problem, then after a while2seconds later the thread1increment1the output is2There is a problem, why did the thread appear in the middle?2What's more strange is the thread2the initial output is1increment13!In fact, this is the problem caused by reentrancy. Don't worry, let's analyze the reason.
first, the timer starts the timer, and then starts a thread1execution method, when the thread1After the first output, at this time the thread1sleeps2seconds, at this time, the timer is not idle because the set time interval is1seconds, when the thread1sleeps1seconds later, the timer starts another thread2execution method, thread2does not care about the thread1whether it is in execution or sleep state, so the thread2is1because the output of the thread1is still in the sleep state and has not incremented. Then after another1seconds, at this time, two events occur simultaneously, the thread1the incremented output after the sleep state is2the timer also starts another thread33the output is the thread1the incremented value21seconds, thread2231Second... I'm almost dizzy, it's about this meaning, what I want to express is: A thread started by a Timer is not yet completed, and another Timer will continue to enter the method for processing when the time comes.
How to solve this problem? There are three solutions, which will be explained one by one, adapted to different scenarios. However, the last one is recommended as it is safer.
★Solution to Reentrancy Problem
1Use the lock(Object) method to prevent reentrancy, indicating that a Timer processing is in progress, and wait for execution when the next Timer occurs and finds that the previous one has not been executed yet. It is suitable for scenarios where reentrancy occurs rarely (specifically, it has not been studied, and it may take up more memory).
The code is similar to the above, add lock to the triggered method, so that when the thread2Enter the triggered method, find that it has been locked, and wait for the code in the lock to be executed before proceeding, the code is as follows:
private static object locko = new object(); /// <summary> /// The callback method of System.Timers.Timer /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private static void TimersTimerHandler(object sender, EventArgs args) { int t = ++num; lock (locko) { Console.WriteLine(string.Format("Thread {0} output:",1}, Output Time: {2}2000); outPut++; Console.WriteLine(string.Format("Thread {0} increment",1Post Output: {1}2}
Execution Result:
2Set a flag to indicate that a Timer processing is in progress, and give up (note that it is giving up, not waiting, see the execution result to understand the meaning) when the next Timer occurs and finds that the previous one has not been executed yet. It is suitable for scenarios where reentrancy occurs frequently.The code is as follows:
private static int inTimer = 0; /// <summary> /// The callback method of System.Timers.Timer /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private static void TimersTimerHandler(object sender, EventArgs args) { int t = ++num; if (inTimer == 0) { inTimer = 1; Console.WriteLine(string.Format("Thread {0} output: {1}, Output Time: {2}, Output Time: { System.Threading.Thread.Sleep(2000); outPut++; Console.WriteLine(string.Format("Thread {0} increment1Post Output: {1}2}, Output Time: { inTimer = 0; } }
Execution Result:
3In a multi-threaded environment, assigning values to inTimer is not safe enough, Interlocked.Exchange provides a lightweight thread-safe method for assigning values to objects (it feels quite sophisticated, and it is also a recommended method), the execution result is similar to the method2Similarly, it also gives up execution.Reference to Interlocked.Exchange usage can be found here.
private static int inTimer = 0; /// <summary> /// The callback method of System.Timers.Timer /// </summary> /// <param name="sender"></param> /// <param name="args"></param> private static void TimersTimerHandler(object sender, EventArgs args) { int t = ++num; if (Interlocked.Exchange(ref inTimer, 1) == 0)} { Console.WriteLine(string.Format("Thread {0} output: {1}, Output Time: {2}, Output Time: { System.Threading.Thread.Sleep(2000); outPut++; Console.WriteLine(string.Format("Thread {0} increment1Post Output: {1}2}, Output Time: { Interlocked.Exchange(ref inTimer, 0); } }
Execution Result:
★Summary
Finally, I have finished typing, it's really not easy. Writing a blog is a very energy-consuming thing, I truly admire those big guys who keep writing tirelessly, salute! Here I will briefly summarize, timer is a very simple class to use, ready to use, here mainly summarizes the solution to the reentrancy problem when using timer, I haven't thought about this problem before, the solution is also very simple, here are three listed, don't know if there are other ways. The solution also applies to the reentrancy problem of multithreading.
★Reference
Here is a list of references not mentioned in the article, thank you for the wisdom of the predecessors!
Reentrancy of Timer Callback Method in ASP.NET
That's all for this article. I hope the content of this article can bring some help to everyone's learning or work, and I also hope to get more support for the Yelling Tutorial!
Declaration: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been edited by humans, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email for reporting, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.)