Multithreading (basic Article 3) and multithreading (basic article 3)

Source: Internet
Author: User

Multithreading (basic Article 3) and multithreading (basic article 3)

In the previous multi-thread (basic article 2), we mainly talked about the knowledge of determining the thread status, thread priority, foreground thread and background thread, and passing parameters to the thread, this article describes how to use the lock keyword of C # to lock the thread, use Monitor to lock the thread, and handle exceptions in the thread.

9. Use the lock keyword of C # to lock the thread

1. Use Visual Studio 2015 to create a new console application.

2. Double-click the "Program. cs" file and modify it to the following code:

 1 using System; 2 using System.Threading; 3 using static System.Console; 4  5 namespace Recipe09 6 { 7     abstract class CounterBase 8     { 9         public abstract void Increment();10         public abstract void Decrement();11     }12 13     class Counter : CounterBase14     {15         public int Count { get; private set; }16 17         public override void Increment()18         {19             Count++;20         }21 22         public override void Decrement()23         {24             Count--;25         }26     }27 28     class CounterWithLock : CounterBase29     {30         private readonly object syncRoot = new Object();31 32         public int Count { get; private set; }33 34         public override void Increment()35         {36             lock (syncRoot)37             {38                 Count++;39             }40         }41 42         public override void Decrement()43         {44             lock (syncRoot)45             {46                 Count--;47             }48         }49     }50 51     class Program52     {53         static void TestCounter(CounterBase c)54         {55             for (int i = 0; i < 100000; i++)56             {57                 c.Increment();58                 c.Decrement();59             }60         }61 62         static void Main(string[] args)63         {64             WriteLine("Incorrect counter");65             var c1 = new Counter();66             var t1 = new Thread(() => TestCounter(c1));67             var t2 = new Thread(() => TestCounter(c1));68             var t3 = new Thread(() => TestCounter(c1));69             t1.Start();70             t2.Start();71             t3.Start();72             t1.Join();73             t2.Join();74             t3.Join();75             WriteLine($"Total count: {c1.Count}");76 77             WriteLine("--------------------------");78 79             WriteLine("Correct counter");80             var c2 = new CounterWithLock();81             t1 = new Thread(() => TestCounter(c2));82             t2 = new Thread(() => TestCounter(c2));83             t3 = new Thread(() => TestCounter(c2));84             t1.Start();85             t2.Start();86             t3.Start();87             t1.Join();88             t2.Join();89             t3.Join();90             WriteLine($"Total count: {c2.Count}");91         }92     }93 }

3. Run the console application. The running effect may be different each time, as shown in:

In the code of Row 3, we created an object of the Counter class, which defines a simple counter variable, which can be increased by 1 and reduced by 1. Then ~ In 68 lines of code, we created three threads and passed the Counter object to the "TestCounter" method using lambda expressions. These three threads share the same counter variable, perform auto-increment and auto-increment operations on the variable, which leads to incorrect results. If we run this console program multiple times, it will print different counter values, which may be 0, but not in most cases.

This occurs because the Counter class is non-thread-safe. Let's assume that the first thread has executed 57th lines of code after it has completed the execution of 58th lines of code, and the second thread has also executed 57th lines of code, at this time, the counter variable value is automatically increased by two times, and the two threads execute code at the same time, which causes the counter variable to be automatically reduced by only one time. Therefore, the result is incorrect.

To ensure that the preceding errors do not occur, we must ensure that when a thread accesses the counter variable, all other threads must wait until the execution is complete before access can continue, we can use the lock keyword to complete this function. If we lock an object in a thread, all other threads must wait until the thread is unlocked to access this object. Therefore, the above situation can be avoided. However, it should be noted that using this method will seriously affect the program performance. A better way will be discussed in xiantong synchronization.

10. Use Monitor to lock the thread

In this section, we will describe a common problem in multi-threaded programming: deadlock. We first create a deadlock example, and then use Monitor to avoid deadlock.

1. Use Visual Studio 2015 to create a new console application.

2. Double-click to open the "Program. cs" file and write the code as follows:

 1 using System; 2 using System.Threading; 3 using static System.Console; 4 using static System.Threading.Thread; 5  6 namespace Recipe10 7 { 8     class Program 9     {10         static void LockTooMuch(object lock1, object lock2)11         {12             lock (lock1)13             {14                 Sleep(1000);15                 lock (lock2)16                 {17                 }18             }19         }20 21         static void Main(string[] args)22         {23             object lock1 = new object();24             object lock2 = new object();25 26             new Thread(() => LockTooMuch(lock1, lock2)).Start();27 28             lock (lock2)29             {30                 WriteLine("This will be a deadlock!");31                 Sleep(1000);32                 lock (lock1)33                 {34                     WriteLine("Acquired a protected resource succesfully");35                 }36             }37         }38     }39 }

3. Run the console application. The running effect is shown in:

In the above results, we can see that the program has a deadlock and the program has never ended.

In 10th ~ In 19 lines of code, we define a method named "LockTooMuch". In this method, we lock the first object lock1 and wait for 1 second to lock the second object lock2.

At line 1 of code, we created a new thread to execute the "LockTooMuch" method and then immediately execute line 3 of code.

In 28th ~ In the 32 lines of code, we lock the object lock2 in the main thread, and then wait for 1 second to lock the first object lock1.

In the new thread we created, we locked the object lock1 and waited for 1 second. We wanted to lock the object lock2. At this time, the object lock2 was locked by the main thread, therefore, the new thread will wait for the object lock2 to be unlocked by the main thread. However, in the main thread, we lock the object lock2 and wait for 1 second to lock the object lock1. At this time, the object lock1 has been locked by the created thread, therefore, the main thread will wait for the thread created by the object lock1 to be unlocked. When this happens, the deadlock occurs, so our console application cannot end normally at present.

4. To avoid deadlocks, we can use "Monitor. tryEnter to replace the lock keyword, "Monitor. the TryEnter method does not block the wait when the requested resource is not available. You can set the timeout time and return false if no resource is obtained. The modification code is as follows:

 1 using System; 2 using System.Threading; 3 using static System.Console; 4 using static System.Threading.Thread; 5  6 namespace Recipe10 7 { 8     class Program 9     {10         static void LockTooMuch(object lock1, object lock2)11         {12             lock (lock1)13             {14                 Sleep(1000);15                 lock (lock2)16                 {17                 }18             }19         }20 21         static void Main(string[] args)22         {23             object lock1 = new object();24             object lock2 = new object();25 26             new Thread(() => LockTooMuch(lock1, lock2)).Start();27 28             lock (lock2)29             {30                 WriteLine("This will be a deadlock!");31                 Sleep(1000);32                 //lock (lock1)33                 //{34                 //    WriteLine("Acquired a protected resource succesfully");35                 //}36                 if (Monitor.TryEnter(lock1, TimeSpan.FromSeconds(5)))37                 {38                     WriteLine("Acquired a protected resource succesfully");39                 }40                 else41                 {42                     WriteLine("Timeout acquiring a resource!");43                 }44             }45         }46     }47 }

5. Run the console application. The running effect is shown in:

In this case, our console application can avoid deadlocks.

11. Handling exceptions

This section describes how to correctly handle exceptions in a thread. It is very important to correctly place the try/catch Block inside the thread, because it is usually impossible to catch exceptions inside the thread outside the thread.

1. Use Visual Studio 2015 to create a new console application.

2. Double-click the "Program. cs" file and modify the Code as follows:

 1 using System; 2 using System.Threading; 3 using static System.Console; 4 using static System.Threading.Thread; 5  6 namespace Recipe11 7 { 8     class Program 9     {10         static void BadFaultyThread()11         {12             WriteLine("Starting a faulty thread...");13             Sleep(TimeSpan.FromSeconds(2));14             throw new Exception("Boom!");15         }16 17         static void FaultyThread()18         {19             try20             {21                 WriteLine("Starting a faulty thread...");22                 Sleep(TimeSpan.FromSeconds(1));23                 throw new Exception("Boom!");24             }25             catch(Exception ex)26             {27                 WriteLine($"Exception handled: {ex.Message}");28             }29         }30 31         static void Main(string[] args)32         {33             var t = new Thread(FaultyThread);34             t.Start();35             t.Join();36 37             try38             {39                 t = new Thread(BadFaultyThread);40                 t.Start();41             }42             catch (Exception ex)43             {44                 WriteLine(ex.Message);45                 WriteLine("We won't get here!");46             }47         }48     }49 }

3. Run the console application. The running effect is shown in:

In 10th ~ In the 15 lines of code, we define a method named "BadFaultyThread", which throws an exception and does not use the try/catch Block to catch the exception.

In 17th ~ In the 29 lines of code, we defined a method named "FaultyThread", which also throws an exception, but we caught the exception using the try/catch Block.

In 33rd ~ In the 35 lines of code, we created a thread and executed the "FaultyThread" method in this thread. We can see that in this newly created thread, we correctly caught the exception thrown in the "FaultyThread" method.

In 37th ~ In the 46 lines of code, we have created another thread and executed the "BadFaultyThread" method in the thread, in addition, try/catch blocks are used in the main thread to capture the exceptions thrown in the newly created thread. Unfortunately, exceptions thrown in the new thread cannot be captured in the main thread.

From this we can see that it is usually not feasible to capture exceptions in another thread in one thread.

Now, we will talk about multithreading (basic) and thread synchronization later!

Source code download

Related Article

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.