Beginner's tour-Learning threads (threads and thread pools)

Source: Internet
Author: User
Tags array length readline

A Gentleman of pleasure.

Beginner's tour-Learning threads (threads and thread pools)

The previous article mainly introduced the process and the thread of some basic knowledge, now return to the point, we come to learn the use of a thread, this article is mainly the use of new threads and the way the pool.

Thread

Let's start with simple threading: Using the new method to create a thread, as for undoing the thread, we don't have to tube (I don't know how to manage xd), because the CLR has already managed it for us.

Create

Let's look at a simple example of using threading:

        static void Main (string[] args)        {            thread t1 = new Thread (MENTHOD1);            Thread t2 = new Thread (MENTHOD2);            T1. Start ();            T2. Start ("Thread 2 parameter");            Console.WriteLine ("Id:{0} of the main thread", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");            Console.ReadLine ();        }        static void Menthod1 ()        {            thread.sleep ();            Console.WriteLine ("Thread 1 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");        }        static void Menthod2 (Object obj)        {            thread.sleep ();            Console.WriteLine ("Thread 2 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("Obj:{0}", obj);            Console.WriteLine ("--------------------");        }

We can create a thread with new, and then use the start () method to run the thread, the thread will execute the Method1 method in its life cycle, the execution method must take time, but the Method1 method is too simple, We use the Thread.Sleep method to pause, this method can temporarily sleep the current thread for a period of time (in milliseconds), because the main thread is just to create and run the T1 sub-thread, run the task is not the main thread, so the main thread can continue to execute the program.

We can also pass a parameter to the thread execution method, such as Thread 2, when the T2 executes the start method, passing in the parameter that you want to pass in, and then can be used at run time, but the parameter is limited, the method of the child thread can only accept parameters of type object, You need an explicit conversion type when you use it, and you can only accept one parameter, and multiple parameters will not support it.

Threads and lambda expressions

The new thread also supports lambda expressions, and if the execution method is simpler, or in some scenarios, we can use lambda to build the code that executes the thread into the new:

        static void Main (string[] args)        {            thread t1 = new Thread (() =            {                thread.sleep);                Console.WriteLine ("Thread 1 of Id:{0}", Thread.CurrentThread.ManagedThreadId);                Console.WriteLine ("--------------------");            });            T1. Start ();            Console.WriteLine ("Id:{0} of the main thread", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");            Console.ReadLine ();        }

There is also a good thing about writing here, which is that you can directly use the variables in the main method, which, of course, creates a thread-safe problem.

Thread synchronization

Multiple threads in a process are other resources that can access their processes, and multithreading is performed concurrently if they are not controlled, and it is easy to create thread-safe problems when the multithreaded execution method contains operations global variables, static variables, or I/O devices, resulting in unpredictable errors. There is a need for thread synchronization, and here are some ways to synchronize threads.

Join:

We sometimes turn on the n threads for the auxiliary calculation, but want the main thread to wait for all child threads to finish computing, or the relationship between threads is more complex, involving thread blocking and activation, then you can use the join () method to block the main thread and implement one of the simplest threading synchronizations:

        static void Main (string[] args)        {            thread t1 = new Thread (MENTHOD1);            Thread t2 = new Thread (MENTHOD2);            T1. Start ();            T1. Join ();            T2. Start ("Thread 2 parameter");            T2. Join ();            Console.WriteLine ("Id:{0} of the main thread", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");            Console.ReadLine ();        }        static void Menthod1 ()        {            thread.sleep ();            Console.WriteLine ("Thread 1 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");        }        static void Menthod2 (Object obj)        {            thread.sleep (4000);            Console.WriteLine ("Thread 2 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("Obj:{0}", obj);            Console.WriteLine ("--------------------");        }

The above call blocking process: First T1 start, block 2 seconds, then T2 execution, blocking 4 seconds, total blocking 6 seconds, seemingly did not play out the advantages of multi-threading, but it is possible to run before T2 run T1, so the call of join () will depend on the situation, join () is to block the current thread to its current location until the blocking thread ends and the current thread continues to run.

Synchronization events:

In addition to join () to implement blocking and activation between threads, there are synchronous events to handle, and there are two types of synchronization events: AutoResetEvent and ManualResetEvent. The only difference between them is whether the state automatically changes from terminating to non-terminating after activating the thread. AutoResetEvent automatically becomes non-terminating, meaning that a autoresetevent can only activate one thread. and ManualResetEvent to wait until its reset method is called, the state becomes non-terminating, and before that, ManualResetEvent can activate any number of threads: First look at the use of ManualResetEvent:

        static ManualResetEvent Muilreset = new ManualResetEvent (false);            static void Main (string[] args) {thread T1 = new Thread (MENTHOD1); T1.            Start ();            Thread t2 = new Thread (MENTHOD2); T2.            Start ("params");            thread t3 = new Thread (MENTHOD3); T3.            Start ();            Muilreset.waitone ();            Console.WriteLine ("Id:{0} of the main thread", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");        Console.ReadLine ();            } static void Menthod1 () {muilreset.waitone ();            Console.WriteLine ("Thread 1 of Id:{0}", Thread.CurrentThread.ManagedThreadId);        Console.WriteLine ("--------------------");            } static void Menthod2 (Object obj) {muilreset.waitone ();            Console.WriteLine ("Thread 2 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("Obj:{0}", obj); Console.WriteLine ("--------------------");            } static void Menthod3 () {thread.sleep (3000);            Console.WriteLine ("Thread 3 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("Activate thread ...");            Console.WriteLine ("--------------------");        Muilreset.set (); }

The above example we block the main thread, thread 1, thread 2, use thread 3 to activate all threads after 3 seconds, show success

Thread 3 of the id:12 activation thread ...--------------------thread 2 of the id:11 obj:params--------------------the Id:9--------------------thread 1 of the main thread The Id:10--------------------

If you use AutoResetEvent, you can only activate the main thread

Thread 3 id:12 activation thread ... the Id:9--------------------of the main thread of the--------------------

Note:ManualResetEvent sends a signal to all referenced threads (multiple threads can share a single manualresetevent, and when ManualResetEvent calls set (), all threads are awakened). The AutoResetEvent only randomly sends one signal (only one is awakened).

Thread synchronization here can also use delegates and events (recommended events) to implement simple communication between threads, such as sending more information through events to another or multiple threads after a thread executes to a node.

Monitor:

The example above is that there are no public resources (public variables, I/O devices, etc.) between the various sub-threads, and they only exist in order of execution; Let's find an example that uses a public variable:

        static list<int> ids = new list<int> ();        static void Main (string[] args)        {            Console.WriteLine ("Id:{0} of the main thread", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");            for (int i = 0; i < i++)            {                thread t = new Thread (menthod);                T.start ();            }            Console.ReadLine ();        }        static void Menthod ()        {            IDs. ADD (Thread.CurrentThread.ManagedThreadId);            Console.WriteLine (IDs. Count);            Console.WriteLine (Ids[0]);            Console.WriteLine ("--------------------");            Ids. Clear ();        }

This creates a new 100 sub-thread, and then uses a static public variable list to output the ID of the thread, which, when run in the above method, sometimes reports an error: The index is out of range. Must be non-negative and less than the collection size! When explaining the current sub-thread output ID, the collection is clear, which is a simple thread-safety issue, so you need to use monitor to lock blocks of code, and the MSDN recommendation defines a private initialization that does not change the object variable as an exclusive lock, Because the exclusive lock is meaningless, the following code can be changed to:

        Static ReadOnly Object locker = new Object ();        static list<int> ids = new list<int> ();        static void Main (string[] args)        {            Console.WriteLine ("Id:{0} of the main thread", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");            for (int i = 0; i < i++)            {                thread t = new Thread (menthod);                T.start ();            }            Console.ReadLine ();        }        static void Menthod ()        {            monitor.enter (locker);            Ids. ADD (Thread.CurrentThread.ManagedThreadId);            Console.WriteLine (IDs. Count);            Console.WriteLine (Ids[0]);            Console.WriteLine ("--------------------");            Ids. Clear ();            Monitor.Exit (locker);        }

When a thread enters a locked block of code, it is locked outside, so that the rest of the thread can only wait for the current thread to release the lock, so that the list variable is not Cheng by other lines when it is taken; Although list is a thread-safe class, is a multithreaded operation of the class when there is only one thread operation of the class, but this still avoids the problem of thread safety, because still can not control the sequence of operations, after the purge will definitely error.

Lock

When you call monitor to execute a block of code that can run only one thread, it is still possible to throw an exception, but sometimes it is impossible to terminate the process, and it is a solution to wrap it up with try{}catch{}, and then lock appears, The above example can be rewritten as:

        static void Menthod ()        {            lock (locker)            {                IDs. ADD (Thread.CurrentThread.ManagedThreadId);                Console.WriteLine (IDs. Count);                Console.WriteLine (Ids[0]);                Console.WriteLine ("--------------------");                Ids. Clear ();            }        }

Equivalent to:

        static void Menthod ()        {            try            {                monitor.enter (locker);                Ids. ADD (Thread.CurrentThread.ManagedThreadId);                Console.WriteLine (IDs. Count);                Console.WriteLine (Ids[0]);                Console.WriteLine ("--------------------");                Ids. Clear ();            }            catch (Exception ex)            {            }            finally            {                monitor.exit (locker);            }        }

When the thread enters the lock code block, the Monitor.Enter () method is called, and the exit code block calls the Monitor.Exit () method. In addition, Monitor provides three static methods Monitor.pulse (), Monitor.pulseall (), and monitor.wait () to enable synchronization of a wake mechanism. For the use of these three methods, you can refer to MSDN, I am also in the study, I will not tell the first. Although lock does not have a powerful monitor, but the use is really convenient, here the choice depends on the actual demand.

Add

There are many ways to synchronize threads, such as mutexes. There are a lot of ways to do it later in the study.
  mutexes: mutexes do not have the function of wait,pulse,pulseall, so we cannot use mutexes to implement similar wake-up functions, but mutexes have a relatively large feature, and mutexes are cross-process, So we can use the same mutex on multiple processes on the same machine or even a remote machine.

Thread pool Purpose

As mentioned in the previous article, the thread is composed of thread ID, program counter, register collection and Stack, is an entity in the process, is the basic unit that is dispatched and dispatched by the system independently, the thread does not own the system resources, only has some resources which are essential in the operation, which means that the thread is created and revoked. Need to allocate and empty some resources, it will need to pay a certain amount of space-time consumption, in some large-scale use of threads (CPU-intensive, I/O intensive) process, using the traditional new method will frequently create, revoke threads, although the management of the thread is carried out by the CLR, but it is always affecting performance, In order to reduce the space-time consumption of creation and revocation, the concept of thread pool is introduced: Pooling the threading entity, is to create a quantitative thread entity beforehand, then put into a container, do unified management, no task, the thread is idle (almost ready state), to select an idle thread to execute after the task, The thread is automatically closed after execution (not revoked, just set to idle).

CLR Thread pool

The CLR thread pool is. NET Framework is a very important part, not only can be used by developers, many of their own functions are also implemented by the thread pool; When we delegate the task to the thread pools, we place the task on the thread pool's task queue, and if there are idle threads in the thread pools, the task is delegated to that thread, waiting to be dispatched to CPU execution, if When there are idle threads and the number of threads managed by the thread pool has not reached the upper limit, the thread pool creates a new thread entity, otherwise the task waits in the queue.

Maximum number of Caps: In versions prior to CLR 2.0 SP1, the default maximum number of threads in the thread pool = number of processors *, CLR 2.0 SP1 became the default maximum number of threads = number of processors * 250, thread caps can be changed by using Threadpool.getmax The +threads and Threadpool.setmaxthreads methods allow you to get and set the maximum number of threads for the thread pool.

Use

The use of the thread pool is simpler:

        static void Main (string[] args)        {            ThreadPool.QueueUserWorkItem (new WaitCallback (MENTHOD1));            ThreadPool.QueueUserWorkItem (New WaitCallback (MENTHOD2), "Object");            Console.WriteLine ("Id:{0} of the main thread", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");            Console.ReadLine ();        }        static void Menthod1 (Object obj)        {            thread.sleep (a);            Console.WriteLine ("Thread 1 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");        }        static void Menthod2 (Object obj)        {            thread.sleep (4000);            Console.WriteLine ("Thread 2 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("Obj:{0}", obj);            Console.WriteLine ("--------------------");        }

Here the QueueUserWorkItem method needs to pass in a QueueUserWorkItem delegate (with an object type parameter, no return value), so we need the thread to execute the task with an object parameter, And there is an overload when the QueueUserWorkItem method is added, you can pass in a parameter here.

Of course, you can also use lambda expressions here:

        ThreadPool.QueueUserWorkItem (new WaitCallback (object obj) =>{            thread.sleep (a);            Console.WriteLine ("Thread 3 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("Obj:{0}", obj);            Console.WriteLine ("--------------------");        }), "Lambda");
Thread synchronization

Concurrent execution of tasks using the thread pool is also subject to thread-safety issues, as well as synchronization, in the case of threads using public resources, Monitor, lock and other methods as used by the above thread, the same can achieve the desired effect, do not repeat the introduction, but for the control of the order of execution, This is not free to use the new thread.

Synchronization events:

In the thread pool, there is no join method, and if you want to control the execution order of threads, I recommend using the main thread to wait for the thread pool task to complete, blocking the main thread, where you can use WaitHandle:

        static void Main (string[] args) {list<waithandle> handles = new list<waithandle> ();            AutoResetEvent AutoReset1 = new AutoResetEvent (false);            ThreadPool.QueueUserWorkItem (New WaitCallback (MENTHOD1), AUTORESET1); Handles.            ADD (AUTORESET1);            AutoResetEvent AutoReset2 = new AutoResetEvent (false);            ThreadPool.QueueUserWorkItem (New WaitCallback (MENTHOD2), AutoReset2); Handles.            ADD (AutoReset2); WaitHandle.WaitAll (handles.            ToArray ());            Console.WriteLine ("Id:{0} of the main thread", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");        Console.ReadLine ();            } static void Menthod1 (Object obj) {thread.sleep (2000);            Console.WriteLine ("Thread 1 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");            AutoResetEvent handle = (AutoResetEvent) obj; HaNdle.        Set ();            } static void Menthod2 (Object obj) {thread.sleep (4000);            Console.WriteLine ("Thread 2 of Id:{0}", Thread.CurrentThread.ManagedThreadId);            Console.WriteLine ("--------------------");            AutoResetEvent handle = (AutoResetEvent) obj; Handle.        Set (); }

Here, each related thread in the thread pool is created with a autoresetevent that, after execution, turns the autoresetevent of itself to non-terminating, WaitHandle uses the WaitAll method to block the main thread, Wait for all AutoResetEvent events to become true, and WaitHandle has another WaitAny method blocking, but as long as one of the threads ends, it continues to run and no longer blocks.

Note:

1. WaitHandle can also be used to create synchronization events for new threads;

2, WaitHandle wait Method (WaitAll, WaitAny) The number of array length must be less than or equal to 64, in order to solve this limitation, there are netizens encapsulated a class, more useful:

mutiplethreadresetevent

Add

1. Threads have foreground thread and background thread, the thread Cheng created with new is considered foreground thread (can be changed using the IsBackground property), and inside the thread pool are background threads

Foreground thread: The foreground thread will not be closed immediately, and its closure will only occur when its execution is complete and unaffected by external factors. If the application exits, causing its foreground thread to terminate, the CLR remains active and running, allowing the application to continue running, and the entire process will be destroyed when its foreground thread terminates.

Background thread: A background thread can be closed by the CLR at any time without throwing an exception, that is, when the background thread is closed, the recycle of the resource is immediate, does not wait, and does not consider whether the background thread is finished or not, even if it is being executed and is immediately terminated.

2, the thread is dispatched by the system to the CPU execution priority: Here the priority is not priority execution, but is scheduled to the CPU execution of high probability; Use new to create threads and thread pool priority is normal, but the former can be set by the priority property. There are 5 levels of priority: Highest, AboveNormal, Normal, BelowNormal, and lowest.

3, the thread exists Suspend and resume these two outdated methods, but not the representative cannot use, but Microsoft does not recommend you to use, MSDN gives the reason is: Please do not use the Suspend and resume method to synchronize thread activity. There is no way to know when you pause the execution of a thread what code. If you hold a lock during security privilege evaluation, the AppDomain may be blocked from other threads in your suspended thread. If you execute a class constructor, you suspend threads in other threads in the AppDomain when you try to use a class that is blocked. Deadlocks can occur very easily . You can ignore this warning to continue using these two methods for thread synchronization, if you feel that it is not reliable, then you can add the thread code to ensure that the correctness of the execution, or use Control synchronization events (AutoResetEvent, etc.) to achieve synchronization.

4, thread pool threads are very valuable, because the number is limited, it is not suitable for long-term job tasks, suitable for short-run and frequent job tasks, if you want to perform long-time job tasks, we recommend the use of new to create the way. After all, the thread pool design is designed to solve the resource waste caused by frequent creation and revocation of threads.

Beginner's tour-Learning threads (threads and thread pools)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.