Analyze the problem of timer setting on the server.
Some time ago, I saw an article in my blog about how to set a timer on the server so that the server can work regularly. Article At that time, I felt very good and provided a good solution for us to do some time-consuming things on the backend of the server. However, this is just a practical explanation, without in-depth analysis of some actual problems such as resource and security after timer is used. Before using a timer, let's take a look at how the timer works. Here is an example (note the thread ID of the timer ): Unsafe Public Static Void Main ()
{
Tmiertest ();
}
Private Static Void Tmiertest ()
{
System. Timers. Timer m_timer = New System. Timers. Timer ();
// (System. componentmodel. isupportinitialize) (m_timer). begininit ();
M_timer.enabled = True ;
M_timer.interval = 1100 ;
M_timer.elapsed + = New Elapsedeventhandler (m_timer_elapsed );
// (System. componentmodel. isupportinitialize) (m_timer). endinit ();
For ( Int I = 0 ; I <= 100 ; I ++ )
{
Console. writeline ("Main:"+I. tostring ()+", Thread ID:"+Appdomain. getcurrentthreadid (). tostring ());
Thread. Sleep (500);
}
}
Private Static Void M_timer_elapsed ( Object Sender, elapsedeventargs E)
{
Console. writeline ("Thread:"+Datetime. Now. tostring ()+", Thread ID:"+Appdomain. getcurrentthreadid (). tostring ());
Thread. Sleep (1000);
}
Running result:
In the same application domain, we can see that the IDs of the uniform threads of the designer are different. That is to say, the timer uses a new thread. However, in such a test, I did not clean up the timer, but did not report the resource leakage during debugging. I don't know why it may be a feature of a timer. I will not discuss its resource leakage here, but discuss its thread nature.
The main function and timer function can share resources, which is also easy to understand. Let's look at a simple example (CodeYou can add a global variable from the preceding modification and access it on the main function and the thread function at the same time.): Only results are given ,:
We can see that they have performed insecure access to the same global data. Why? Here, I already think that the timer is a new thread. Therefore, the above access method is not safe.
However, when a timer is set on the server, this problem occurs. If the timer only does something that has nothing to do with IIS and has nothing to do with user requests, then the timer settings are meaningless (no one will be so boring to add a useless timer on the server to waste server resources ). The problem occurs when timer processing functions and resource sharing. Let's take a look at this example: // Class Name: Sample
Private Static Sample m_object;
Unsafe Public Static Void Main ()
{
Tmiertest ();
}
Public Void Writeline ( String I _msg)
{
Console. writeline ("This is a test output. Called thread ID:"+I _msg );
}
Private Static Void Tmiertest ()
{
M_object = New Sample () As Sample;
System. Timers. Timer m_timer = New System. Timers. Timer ();
// (System. componentmodel. isupportinitialize) (m_timer). begininit ();
M_timer.enabled = True ;
M_timer.interval = 1100 ;
M_timer.elapsed + = New Elapsedeventhandler (m_timer_elapsed );
// (System. componentmodel. isupportinitialize) (m_timer). endinit ();
For ( Int I = 0 ; I <= 100 ; I ++ )
{
Console. writeline ( " Main: " + I. tostring () + " , Thread ID: " + Appdomain. getcurrentthreadid (). tostring () + " , M_count: " + (M_count ++ ). Tostring ());
M_object.writeline (appdomain. getcurrentthreadid (). tostring ());
Thread. Sleep ( 500 );
}
// M_object = NULL;
For ( Int I = 0 ; I <= 100 ; I ++ )
{
Console. writeline ( " Main: " + I. tostring () + " , Thread ID: " + Appdomain. getcurrentthreadid (). tostring () + " , M_count: " + (M_count ++ ). Tostring ());
Thread. Sleep ( 500 );
}
}
Private Static Void M_timer_elapsed ( Object Sender, elapsedeventargs E)
{
Console. writeline ( " Thread: " + Datetime. Now. tostring () + " , Thread ID: " + Appdomain. getcurrentthreadid (). tostring () + " , M_count: " + (M_count ++ ). Tostring ());
M_object.writeline (appdomain. getcurrentthreadid (). tostring ());
Thread. Sleep ( 1000 );
}
Running result:
Note: m_object = NULL;
If this sentence is not commented out, what are the consequences? To my surprise, nothing happened! AndProgramIt can run normally, but there is no test output. This indicates that the resource is invalid, but the program does not have any problems, that is, the timer works the same way. As you can imagine, this kind of work is no longer valid, and no error feedback is obtained (only a little pause when m_object = NULL, debugging does not give an error ). Only the try-catch-structure can be used to obtain the error information for further processing. However, this problem is also very serious.
now we should be able to clearly understand what is the consequence of adding a timer to the server.
here is a description of the class:
httpcontent. current. request;
the help is clear that it can only be used in the Asp.net program and cannot be inherited.
if you use it globally, you can use it easily to obtain the current user's request and process some events.
However, if the current user's request has been stopped or the user's session has been terminated, all the processing in the timer will be non-correct, however, such an error rarely occurs, because you cannot use try-catch to handle all your work. Even so, it is unclear when such an error will occur, in addition to cleaning timers and some resources, when an error occurs, you can hardly do anything else. After my many tests (using a server timer for several weeks), I finally thought it was too unstable and the error rate was extremely high, in addition, it is inexplicable to find many errors in the error log.
the most serious problem is that its errors will seriously affect the normal operation of IIS. That is to say, if the timer is incorrect, IIS access will also encounter errors (when resources are shared, such as database links, this is almost absolute ).
after tests and analysis over the past few weeks, I still feel that using a timer on the server is really not a good deal. Although theoretically feasible, in addition, it still works well in a short period of time, but the long-term effect is really poor, and the impact on the entire application is also very serious. Not to mention encapsulating the timer in the class, this approach is almost disastrous (I did it ).
give the Error Log:
1/5/2006 11:02:31 PM error in severfordeleteedfiles: unspecified error
1/5/2006 11:07:52 PM stop the wavesystemservice.
1/5/2006 11:07:52 system. eventargs
1/6/2006 12:42:07 am system. eventargs
1/6/2006 12:47:53 am unspecified error
1/6/2006 12:48:06 am unspecified error
1/6/2006 10:01:33 am system. eventargs
1/6/2006 10:01:38 am system. eventargs
1/6/2006 10:18:53 am error in severfordeleteedfiles: unspecified error
1/6/2006 10:35:33 am error in severfordeleteedfiles: unspecified error
1/6/2006 10:52:13 am error in severfordeleteedfiles: unspecified error
1/6/2006 11:08:53 am error in severfordeleteedfiles: unspecified error
1/6/2006 11:20:26 am system. eventargs
1/6/2006 4:31:59 PM do file clean up for client: 21
1/6/2006 4:48:39 PM error in severfordeleteedfiles: unspecified error
1/6/2006 5:17:09 system. eventargs
1/6/2006 5:24:20 stop the wavesystemservice.
To put it simply, in desperation, I used the catch structure in the entire function to get such an unspecified error message. At the same time, IIS requests also produce the same error. In short, the server cannot use a timer, at least I do not dare to use it.