C # is a generic development tool for the .NET platform, which is capable of building all .NET applications. All threads in .NET run in AppDomain, this may make you think of the Win32 process, in fact they still have a big difference. The application field provides a secure and universal processing unit, and the public language runtime can use it to isolate the application. Note that the application of the application in .NET is the application domain instead of the process, there can be several application domains in a single process, and the thread can span the scope of the application domain, and the method in a thread can call another thread. Method, this is not an additional overhead such as inter-process calls or inter-process switching. It can be said that the application domain is a logical process within the physical process (that is, the process in Win32). In Visul C #, system.threading namespace provides some classes and interfaces that make multithreaded programming, where threads have the following methods: Thread, ThreadPool, Timer. Below I will make a brief introduction on their usage. 1. Thread This is perhaps the most complex method, but it provides a variety of flexible control of threads. First, you have to create a thread instance using its constructor. Its parameters are relatively simple, only one threadstart delegate: [threadstart start "; then call start () Start it, of course you can take advantage of its priority property To set or get its operating priority (Enum ThreadPriority: Normal, Lowest, Highest, Belownormal, ABOVENORMAL). See here: It first generates two thread examples T1 and T2, then set their priority, then start the two-thread (the two threads are basically the same, but they output different, T1 is "1", T2 is "" 2, depending on their respective output characters, it can generally see that they take up the ratio of CPU time, 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" dosth (); console.write ("1");}}}}}}}}}}}} public static void thread2 () {for (int i = 0; i <1000; i ) {//// Write a "2" dosth (); console.write ("2");}}}}}}}}} public static void dosth ()} {// Used to simulate complex operation for (int J = 0; J <10000000; j ) {int a = 15; a = a * a * a * a;}} over run results: 11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112
11111111111111111111111111111111111111111121111111111111111111111111111111111111111112 we can see from the above results, and more CPU time than t1 and t2 occupied by threads, this is because of the high priority than t1 and t2, if we put the priority of t1 and t2 are set to Normal, that results how? Is the CPU time they occupied? Yes, just as you can, see the figure below:
1212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121212121
212121212121212121212121212121212121212121212121212121212121212121212
12121212121212121212 We can see from the above example, its structure is similar to Win32's working thread, but it is more simple, just use the function to be called as a delegate, and then construct a thread instance as a parameter. When the call start () is started, the corresponding function will be called, starting from the first line of the function. Next we combine the thread's threadState property to understand the thread control. ThreadState is an enumeration type that reflects the status of threads. When a thread instance is just created, its threadState is unstearted; after this thread is called start () starts, its threadState is running; after this thread is started, if you want to pause (block), you can call Thread. Sleep () method, it has two overload methods (INT), SLEEP (TimeSpan), but it means that the amount of time is different, when this function is called in a thread, it indicates that this thread will Blocking for a period of time (time is determined by the number of millisess or timespan passed to Sleep, but if the parameter is 0, the thread is suspended to enable other threads to execute, specify the infinitely blocking thread), at this time it's ThreadState It will become waitsleepjoin, and it is also worth noting that the sleep () function is defined as static? ! This also means that it can't be used with a thread example, that is, there is no call similar to T1.Sleep (10)! As soed, the SLEEP () function can only be called by the thread that needs "Sleep", and other thread calls are not allowed.
However, when a thread is in the WaitSleepjoin state, it has to wake up it, it can use the Thread.Interrupt method, which will trigger ThreadInterruptedException on the thread. Let's take an example (note the call method of Sleep): 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 autoreteevent e = new automatic void thread1 () {Try {// From the parameter can be seen to cause sleep Thread.Sleep (Timeout.infinite);} catch System.Threading.ThreadInterruptedException E) {// Interrupt handler Console.Writeline ("1st interrupt");} E.SET (); try {// Sleep Thread.sleep (timeout.infinite);} catch (system.threading .ThreadInterruptedExcection e) {console.writeLine ("2nd interrupt");} // Pause 10 second thread.sleep (10000);} Run result is: 1st interrupt 2nd interrupt (after 10S) T1 is end from the previous example we can see The thread.interrupt method can wake up the program from a waitsleepjoin state to the corresponding interrupt handler, then continue to execute down (its threadst " The ATE has also changed to running). This function must be aware of the following points: 1. This method not only wakes up from the block caused by Sleep, but it is effective for everything that can cause threads into the WaitSleepJoin state (such as Wait and Join). As shown in the above example, the method that causes the thread blocking occurs in the TRY block and places the corresponding interrupt handler into the CATCH block. 2. Calling an Interrupt for a certain thread, if it is in a waitSleepJoin state, then enter the corresponding interrupt handler execution, if it is not in the waitSleepJoin state, it will be interrupted immediately when it enters this state. If the interrupt is called before the interrupt, only the first call is valid, this is the reason why I use synchronization, so that the second call of Interrupt is called after the first interrupt, otherwise it may cause the second time. The call is invalid (if it is called before the first interrupt). You can try the synchronous to try, the result is probably: 1st Interrupt's method also uses two other methods that make the thread into the WaitsleepJoin state: use the synchronization object and the thread.join method.
The use of the JOIN method is relatively simple, it represents the current thread blocking of this method until the other thread (this is T1) terminates or passes the specified time (if it has a time quantity parameter), Conditions (if any) appears, it immediately ends the waitsleepjoin status into the Running state (which can be determined by the return value of the .join method, which is TRUE, the thread termination; false is time to). The thread pause can also use the thread.suspend method, when a thread calls the Suspend method when a thread is in the Running state, it will enter the SUSPENDREQUESTED state, but it will not be hang immediately until the thread arrives at the security point. The thread hangs, and it will enter the SUSPENDED state. If you call a Suspended thread, it is invalid. To recover run, just call thread.resume. Finally, we talk about the destruction of the thread, we can call the Abort method on the thread that needs to be destroyed, which will trigger ThreadAbortException in this thread. We can put some of the code in the thread into the TRY block and put the corresponding processing code into the corresponding CATCH block, when the thread is executing the code in the TRY block, it will jump into the corresponding CATCH block. Internal execution, it will be terminated after executing the code in Catch fast (if resetabort is executed), it will cancel the current Abort request, continue to do it down. So to make sure that a thread is termination is termination. JOIN , As in the above example). 2. ThreadPool Pool is a relatively simple method that adapts to some of the need for multiple threads and short tasks (such as threads that are often blocked), it is that it cannot be controlled for created threads It also cannot set its priority. Since there is only one thread pool for each process, there is only one thread pool (line) per application domain, so you will find the member function of the Threadpool class is static! When you first call ThreadPool.QueueUserWorkItem, ThreadPool.RegisterWaitForsingleObject, the thread pool instance is created. Let me introduce the two functions in the thread pool: [c #] public static bool QueueUserWorkItem (// Call success Returns TrueWaitCallback callback, // To create thread calls to be invoked Object State // pass to the parameters of the entrusted parameters) / / Another overload function is similar, but it has been commissioned without parameters. This function is to queue the thread to create to the thread pool. When the number of available threads of the thread pool is not zero (the thread pool has the number of threads) Restrictions, the missing value is 25), create this thread, otherwise queuing the thread pool, etc., it is created when it is available.
[C #] public static RegisteredWaitHandle RegisterWaitForSingleObject (WaitHandle waitObject, // you want to register WaitHandle WaitOrTimerCallback callBack, // thread calls the delegate object state, // parameters passed to the delegate int TimeOut, // timeout in milliseconds, bool executeOnlyOnce file : // Yes / No only once); public delegate void waitorTimalCallback (Object State, //) Parameters passing to the entrusted parameter BOOL TIMEDOUT // True means that due to the timeout call, it is because WaitObject); this function is created A wait thread, once called this function, create this thread, before the parameter waitObject becomes the termination or set time Timeout, it is in the "block" state, it is worth noting that this "blocked" with thread Waitsleepjoin status is very different: When the THREAD is in the WaitSleepJoin state, the CPU will regularly wake up it to poll the update status information, then enter the WaitsleepJoin state, thread switching is expensive; thread created with this function Then, the CPU will not switch to this thread before triggering it, it does not occupy the CPU time and does not waste the thread switching time, but how does the CPU know when to run it? In fact, the thread pool generates some auxiliary threads to monitor these trigger conditions. Once the condition is reached, the corresponding thread is started, of course, these auxiliary threads themselves take up, but if you need to create more wait threads, use the thread pool The advantage is more obvious. See example: static AutoResetEvent ev = new AutoResetEvent (false); public static int Main (string [] args) {ThreadPool.RegisterWaitForSingleObject (ev, new WaitOrTimerCallback (WaitThreadFunc), 4, 2000, false // wait for the completion of each operation represents After resetting the timer until you log out of the wait); 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 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 thread threadfunc has run once, while WaitThreadFunc has run 5 times. We can determine from the BOOL T parameter from WaitortImerCallback to start this thread. In addition, we can also pass some parameters to threads via Object B. 3. Timer It is suitable for methods that require cyclical calls, which are not run in the thread that creates a timer, which is running in separate threads automatically assigned by the system. This is similar to the SetTimer method in Win32. It constructs: [C #] Public Timer (TimerCallback Callback, // Method Object State, // Pass to Callback "INT DUETIME, // How long does it take to call Callback Int Period // Time to call this method Interval); // If DUETIME is 0, Callback immediately performs its first call. If DUETIME is Infeite, the Callback does not call its method. The timer is disabled, but it can be re-enabled using the Change method. If PERIOD is 0 or Infinite, and Duetime is not Infinite, Callback calls its method once. The timer's regular behavior is disabled, but it can be re-enabled using the Change method. If PERIOD is zero (0) or Infinite, and DUETIME is not in Infinite, the Callback calls its method once. The timer's regular behavior is disabled, but it can be re-enabled using the Change method. If you want to change its Period and Duetime after creating a timer, we can change by calling Timer's Change method: [C #] public bool change; // Obviously the two parameters change corresponding to The two parameters in Timer are shown in the case: public static int main (string [] args) {console.writeline ("Period IS 1000); TimerTM = New Timer (NEW TIMERCALLBACK (TimerCall), 3, 1000, 1000) Thread.Sleep (2000); console.writeline ("Period IS 500"); TM.Change (0,800); thread.sleep (3000); return 0;} public static void timerl (Object B) {Console.Writeline TimerCallback; b IS {0} ", b);} The result of its operation 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