The system. threading namespace provides classes and interfaces that enable multi-threaded programming. There are three methods for creating threads: thread, threadpool, and timer. Next I will give a brief introduction to their usage methods one by one.
1. thread
This may be the most complicated method, but it provides various flexible control over the thread. First, you must use its constructor to create a thread instance. Its parameters are relatively simple and there is only one threadstart delegate:
Public thread (threadstart start );
Then call start () to start him. Of course, you can use his priority attribute to set or obtain his running priority (enum threadpriority: normal, lowest, highest, belownormal, abovenormal ).
See the following example: He first generates two thread instances t1 and t2, then sets their priorities, and then starts two threads (the two threads are basically the same, but their output is different, t1 is "1" and t2 is "2". The ratio of their respective output characters to their cpu usage time is roughly displayed, which also reflects their respective priorities ).
Static void main (string [] args)
{
Thread t1 = new thread (new threadstart (thread1 ));
Thread t2 = new thread (new threadstart (thread2 ));
T1.priority = threadpriority. belownormal;
T2.priority = threadpriority. lowest;
T1.start ();
T2.start ();
}
Public static void thread1 ()
{
For (int I = 1; I <1000; I ++)
{// Write a "1" for each running Loop"
Dosomething ();
Console. write ("1 ");
}
}
Public static void thread2 ()
{
For (int I = 0; I <1000; I ++)
{// Write a "2" for each running Loop"
Dosomething ();
Console. write ("2 ");
}
}
Public static void dosomething ()
{// Used to simulate complex operations
For (int j = 0; j <10000000; j ++)
{
Int a = 15;
A = a *;
}
}
The running result of the above program is:
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
From the above results, we can see that t1 threads occupy much more cpu time than t2, because t1 has a higher priority than t2, if we set the priority of t1 and t2 to normal, what is the result? Will their cpu time be the same? Yes, as expected, see:
121211221212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
121212121212121212
From the preceding example, we can see that the construction of a worker thread is similar to that of win32, but it is simpler to use the function to be called by the thread as the delegate, and then the delegate as the parameter to construct the thread instance. After start () is called, the corresponding function is called and executed from the first line of the function.
Next, we will learn about thread control based on the threadstate attribute of the thread.
Threadstate is an enumeration type, which reflects the state of the thread.
When a thread instance is just created, its threadstate is unstarted; when the thread is called start () to start, its threadstate is running; after the thread is started, if you want him to pause (Block), you can call thread. sleep () method, which has two overload methods (sleep (int) and sleep (timespan), except that the format of the time amount is different, when this function is called in a thread, it indicates that the thread will be blocked for a period of time (the time is determined by the number of milliseconds passed to sleep or timespan, if the parameter is set to 0, the thread is suspended for execution by other threads, and infinite is specified to block the thread indefinitely. In this case, the threadstate changes to waitsleepjoin, in addition, it is worth noting that the sleep () function is defined as static ?! This also means that it cannot be used together with a thread instance, that is, there is no call similar to t1.sleep (10! In this case, the sleep () function can only be called by the thread that requires "sleep". Other threads are not allowed to call the function, just as when to sleep is a private matter that cannot be determined by others. However, when a thread is in the waitsleepjoin state and has to wake up, you can use the thread. the interrupt method will trigger threadinterruptedexception on the thread. Let's take a look at an example (pay attention to the sleep call method ):
Static void main (string [] args)
{
Thread t1 = new thread (new threadstart (thread1 ));
T1.start ();
T1.interrupt ();
E. waitone ();
T1.interrupt ();
T1.join ();
Console. writeline ("t1 is end ");
}
Static autoresetevent e = new autoresetevent (false );
Public static void thread1 ()
{
Try
{// It can be seen from the parameter that it will cause sleep
Thread. sleep (timeout. infinite );
}
Catch (system. threading. threadinterruptedexception e)
{// Interrupt Processing Program
Console. writeline ("1st interrupt ");
}
E. set ();
Try
{// Sleep
Thread. sleep (timeout. infinite );
}
Catch (system. threading. threadinterruptedexception e)
{
Console. writeline ("2nd interrupt ");
} // Pause for 10 seconds
Thread. sleep (10000 );
}
Running result: 1st interrupt
2nd interrupt
(10 s later) t1 is end
We can see from the above example that thread. the interrupt method can wake the program from a blocking (waitsleepjoin) state to the corresponding interrupt Processing Program, and then continue to run (his threadstate also changes to running ), note the following when using this function:
1. This method can not only wake up the blocking caused by sleep, but also be effective for all methods (such as wait and join) that can cause the thread to enter the waitsleepjoin state. As shown in the preceding example, the method that causes thread blocking should be put into the try block, and the corresponding interrupt handler should be put into the catch Block.
2. interrupt is called on a thread. If it is in the waitsleepjoin state, the corresponding interrupt processing program is executed. If it is not in the waitsleepjoin state, then when it enters the state, will be instantly interrupted. If interrupt is called several times before the interruption, only the first call is valid. This is the reason why I used synchronization in the previous example. In this way, the second call to interrupt is called after the first disconnection, otherwise, the second call may be invalid (if it is called before the first interruption ). You can try to remove synchronization. The result is very likely: 1st interrupt
In the preceding example, two other methods are used to bring the thread into the waitsleepjoin state: the synchronization object and the thread. join method. The join method is easy to use. It indicates that the current thread that calls this method is blocked until another thread (t1 in this example) when either of the two conditions (if any) appears, it instantly ends waitsleepjoin and enters the running state (depending on. the Return Value of the join method determines the condition. If it is true, the thread is terminated. If it is false, the time is reached ).
Thread suspension is also available. suspend method. When a thread is in the running state, the suspend method is called for it. It enters the suspendrequested state, but it is not immediately suspended, the thread cannot be suspended until it reaches the security point. At this time, the thread enters the sucanceled ded state. For example, a thread that is already in the susponded state is invalid. to resume the operation, you only need to call thread. resume.
Thread destruction: Call the abort method for the thread to be destroyed, which will cause threadabortexception on this thread. We can put some code in the thread into the try block and put the corresponding processing code into the catch Block. When the thread is executing the code in the try block, if it is called abort, he will jump into the corresponding catch block and execute it. After the code in the catch fast, he will terminate (if the catch Block executes resetabort, it will be different: he will cancel the current abort request and continue to execute. Therefore, to ensure that a thread is terminated with the best join clause, as shown in the preceding example ).
2. threadpool
Provides a thread pool for sending work items, processing asynchronous I/O, representing other threads waiting, and processing timers.
Threadpool is a relatively simple method. It is suitable for some short tasks that require multiple threads (such as some threads that are often blocked ), the disadvantage is that the created thread cannot be controlled or set its priority. Since each process has only one thread pool, and of course each application domain has only one thread pool (line-to-line), you will find that all the member functions of the threadpool class are static! When you call threadpool. queueuserworkitem and threadpool. registerwaitforsingleobject for the first time, a thread pool instance will be created.
Next I will introduce the two functions in the thread pool:
Public static bool queueuserworkitem (// if the call is successful, true is returned.
Waitcallback callback, // Delegate of the thread call to be created
Object state // parameters passed to the Delegate
) // Another overload function is similar, but the delegate does not contain parameters.
This function queues the threads to be created to the thread pool. When the number of available threads in the thread pool is not zero (the thread pool has a limit on the number of threads to be created, if the value is 25), the thread is created. Otherwise, the thread is queued to the thread pool until it has available threads.
Public static registeredwaithandle registerwaitforsingleobject (
Waithandle waitobject, // The waithandle to be registered
Waitortimercallback callback, // delegate for thread call
Object state, // parameters passed to the Delegate
Int timeout, // timeout, in milliseconds,
Bool executeonlyonce file: // yes/no only once
);
Public delegate void waitortimercallback (
Object state, // The parameter passed to the Delegate
Bool timedout // true indicates that the call is timed out, whereas waitobject
);
This function is used to create a waiting thread. Once this function is called, this thread is created. When the waitobject parameter changes to the terminated state or the set time timeout is reached, all of them are in the "blocking" state. It is worth noting that this "blocking" is very different from the waitsleepjoin state of thread: when a thread is in the waitsleepjoin status, the cpu will regularly wake it up to poll and update the status information, and then enter the waitsleepjoin status again. The conversion of the thread is very resource-intensive; the threads created using this function are different. The cpu will not be converted to this thread before it is triggered to run. It does not occupy the cpu time and does not waste the thread conversion time, but how does the cpu know when to run him? In fact, the thread pool will generate some auxiliary threads to monitor these trigger conditions. once the conditions are met, the corresponding threads will be started. Of course, these auxiliary threads also occupy time, however, if you need to create more waiting threads, the advantage of using the thread pool is more obvious. See the following example:
Static autoresetevent ev = new autoresetevent (false );
Public static int main (string [] args)
{Threadpool. registerwaitforsingleobject (
Ev,
New waitortimercallback (waitthreadfunc ),
4,
2000,
False // indicates that the timer is reset every time the wait operation is completed until the logout is completed.
);
Threadpool. queueuserworkitem (new waitcallback (threadfunc), 8 );
Thread. sleep (10000 );
Return 0;
}
Public static void threadfunc (object B)
{Console. writeline ("the object is {0}", B );
For (int I = 0; I <2; I ++)
{Thread. sleep (1000 );
Ev. set ();
}
}
Public static void waitthreadfunc (object B, bool t)
{Console. writeline ("the object is {0}, t is {1}", B, t );
}
The running result is:
The object is 8
The object is 4, t is false
The object is 4, t is false
The object is 4, t is true
The object is 4, t is true
The object is 4, t is true
From the above results, we can see that the thread threadfunc runs once, while the waitthreadfunc runs five times. We can judge the reason for starting this thread from the bool t parameter in waitortimercallback: t is false, it indicates that it is due to waitobject; otherwise, it is due to timeout. In addition, we can pass some parameters to the thread through object B.
3. timer
Provides a mechanism to execute methods at a specified interval.
Use the TimerCallback delegate to specify the method to be executed by the Timer. The timer delegate is specified when the timer is constructed and cannot be changed. This method is not executed on the thread that creates the timer, but on the ThreadPool thread provided by the system.
When creating a timer, you can specify the amount of time (End Time) waiting before the first method is executed and the amount of time (cycle) waiting in the subsequent execution period ). You can use the Change method to Change these values or disable timers.
As long as you are using Timer, you must keep references to it. If there is no reference to Timer for any managed object, the Timer will be reclaimed. Timer is recycled even if it is still active.
When you no longer need a timer, use the Dispose method to release the resources held by the timer. If you want to receive a signal when the timer is released, use the Dispose (WaitHandle) method that accepts WaitHandle to overload it. After the timer is released, WaitHandle is terminated.
The callback method executed by the timer should be reentrant because it is called on the ThreadPool thread. In the following two cases, the callback can be executed simultaneously on two thread pools: first, the timer interval is less than the time required to execute the callback; second, all thread pool threads are in use, and the callback is queued multiple times.
System. Threading. Timer is a simple lightweight Timer that uses the callback method and is provided by the thread pool thread. It is not recommended to use Windows Forms because the callback is not performed on the user interface thread. System. Windows. Forms. Timer is a better choice for Windows Forms. To obtain the server-based Timer function, consider using System. Timers. Timer, which can trigger events and have other functions.
This is similar to the settimer method in win32. Its structure is:
Public timer (
Timercallback callback, // method to be called
Object state, // The parameter passed to callback
Int duetime, // how long will it take to start calling callback?
Int period // The interval at which this method is called
);
// If duetime is 0, callback immediately executes its first call. If duetime is infinite, callback does not call its method. The timer is disabled, but the change method can be used to enable it again. If the period is 0 or infinite and the duetime is not infinite, callback calls his method once. The timer's regular behavior is disabled, but the change method can be used to enable it again. If the period is zero (0) or infinite and the duetime is not infinite, callback calls his method once. The timer's regular behavior is disabled, but the change method can be used to enable it again.
If you want to change the period and duetime after creating a timer, you can call the change method of timer to change it:
[C #]
Public bool change (
Int duetime,
Int period
); // The two parameters obviously changed correspond to the two parameters in timer.
See the following example:
Public static int main (string [] args)
{Console. writeline ("period is 1000 ");
Timer tm = new timer (new timercallback (timercall), 3, 1000 );
Thread. sleep (2000 );
Console. writeline ("period is 500 ");
Tm. change (1, 0,800 );
Thread. sleep (3000 );
Return 0;
}
Public static void timercall (object B)
{
Console. writeline ("timercallback; B is {0}", B );
}
The running result is:
Period is 1000
Timercallback; B is 3
Timercallback; B is 3
Period is 500
Timercallback; B is 3
Timercallback; B is 3
Timercallback; B is 3
Timercallback; B is 3
Summary
From the above brief introduction, we can see their respective use cases: thread applies to those scenarios where complicated control of threads is required; threadpool is applicable to some threads that require multiple threads and short tasks (such as some threads that are often in the blocking state); timer is applicable to the methods that require periodic calls. As long as we understand their usage features, we can select a proper method.
Author: szstephen Zhou