C # thread synchronization with multiple threads 3,

Source: Internet
Author: User

C # thread synchronization with multiple threads 3,

In the previous C # thread synchronization 2, we mainly learned the AutoResetEvent construction, ManualResetEventSlim construction, and CountdownEvent construction. In this article, we will learn about Barrier construction, ReaderWriterLockSlim construction, and SpinWait construction.

7. Use Barrier to construct

In this section, we will learn an interesting synchronization structure: Barrier. The Barrier structure can help us control the number of threads waiting to reach the specified number before sending the notification signal. Then, all the waiting threads can continue to run. After each waiting thread reaches the specified number, you can also execute a callback method. The procedure is as follows:

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 Recipe07 7 { 8     class Program 9     {10         static Barrier barrier = new Barrier(2, b => WriteLine($"End of phase {b.CurrentPhaseNumber + 1}"));11 12         static void PlayMusic(string name, string message, int seconds)13         {14             for(int i = 1; i < 3; i++)15             {16                 WriteLine("----------------------------------------------");17                 Sleep(TimeSpan.FromSeconds(seconds));18                 WriteLine($"{name} starts to {message}");19                 Sleep(TimeSpan.FromSeconds(seconds));20                 WriteLine($"{name} finishes to {message}");21                 barrier.SignalAndWait();22             }23             24         }25 26         static void Main(string[] args)27         {28             var t1 = new Thread(() => PlayMusic("the guitarist", "play an amazing solo", 5));29             var t2 = new Thread(() => PlayMusic("the singer", "sing his song", 2));30 31             t1.Start();32             t2.Start();33         }34     }35 }

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

In the code of Row 3, we created a Barrier instance, barrier, and assigned a value of 2 to the "participant count" parameter of the constructor, indicating that the number of barrier threads involved is 2, that is to say, after two threads reach the blocking level, barrier sends a notification signal and the blocking thread can continue to execute. The second parameter "postPhaseAction" is an Action-type delegate, indicating the callback method to be executed when the number of blocked threads reaches the specified number.

In 28th ~ In 29 lines of code, we created two threads t1 and t2 for executing the "PlayMusic" method. Thread t2 first executes the code at line 2. In this line of code, we call barrier's "SignalAndWait" method in thread t2, the execution can continue only when the number of participating threads reaches the number 2 specified by the constructor, because when the t2 thread calls this method, only one thread t2 is blocked, the number of T2. therefore, T2. When the t1 thread executes the code at line 1, it also calls the barrier's "SignalAndWait" method. At this time, it waits for the number of threads to reach the specified number 2, so t1 and t2 threads can continue to execute, and the callback method specified by the second parameter of the barrier constructor is also executed.

When the two threads execute the second loop of the "PlayMusic" method, the process is the same as that of the first, not described.

8. construct with ReaderWriterLockSlim

In this section, we will learn how to use ReaderWriterLockSlim to construct a thread to safely read and write data in a set using multiple threads. The procedure is as follows:

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. collections. generic; 3 using System. threading; 4 using static System. console; 5 using static System. threading. thread; 6 7 namespace Recipe08 8 {9 class Program10 {11 // indicates the lock status used to manage resource access, supports multi-threaded reading or exclusive Write Access 12 static ReaderWriterLockSlim rw = new ReaderWriterLockSlim (); 13 static Dictionary <int, int> items = new Dictionary <int, int> (); 14 15 static void Read () 16 {17 WriteL Ine ("Reading contents of a dictionary"); 18 while (true) 19 {20 try21 {22 // try to enter read Mode Lock status 23 rw. enterReadLock (); 24 foreach (var key in items. keys) 25 {26 Sleep (TimeSpan. fromSeconds (0.1); 27} 28} 29 finally30 {31 // reduce the recursive count in Read mode, and exit read Mode 32 rw when the generated count is 0 (0. exitReadLock (); 33} 34} 35} 36 37 static void Write (string threadName) 38 {39 while (true) 40 {41 try42 {43 int newKey = new Random (). next (250); 44 // try to enter Upgrade Mode Lock status 45 rw. EnterUpgradeableReadLock (); 46 if (! Items. containsKey (newKey) 47 {48 try49 {50 // try to enter write mode lock status 51 rw. enterWriteLock (); 52 items [newKey] = 1; 53 WriteLine ($ "New key {newKey} is added to a dictionary by a {threadName }"); 54} 55 finally56 {57 // reduce the recursive count of the write mode, and exit the write mode 58 rw when the generated count is 0 (0. exitWriteLock (); 59} 60} 61 Sleep (TimeSpan. fromSeconds (0.1); 62} 63 finally64 {65 // reduce the recursive count in the upgradeable mode, and exit the upgradeable mode 66 rw when the generated count is 0 (0. exitUpgradeableReadLock (); 67} 68} 69} 70 71 static void Main (string [] args) 72 {73 new Thread (Read) {IsBackground = true }. start (); 74 new Thread (Read) {IsBackground = true }. start (); 75 new Thread (Read) {IsBackground = true }. start (); 76 77 new Thread () => Write ("Thread 1") {IsBackground = true }. start (); 78 new Thread () => Write ("Thread 2") {IsBackground = true }. start (); 79 80 Sleep (TimeSpan. fromSeconds (20); 81} 82} 83}

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

In 73rd ~ In 75 lines of code, we created three backend threads to read data from the set. In 77th ~ In 78 lines of code, we have created two background threads to write data to the collection. To operate collections securely, we use the ReaderWriterLockSlim structure specially designed for this scenario. There are two types of locks: Read mode locks and write mode locks. The read Mode Lock allows multiple threads to read data. The Write mode lock blocks every operation of other threads until the write mode lock is released.

There is a very interesting scenario. When we want to obtain a read mode lock to read some data from the set and obtain a write Mode Lock Based on the data to update the Set, if we get the lock mode lock immediately, it will not only consume a lot of time, but also not allow us to read data, because when we get a lock in the write mode, the set will be locked. To minimize the waste of time, we can use the "EnterUpgradeableReadLock" method to obtain the Read mode lock to read data. After reading the data, we find that the underlying set needs to be updated, then we can use "EnterWriteLock" to upgrade our lock, then quickly execute the write operation and use "ExitWriteLock" to release the write mode lock, and finally use "ExitUpgradeableReadLock" to release the upgradeable mode lock.

In the above Code, we obtain a random number, then obtain a read mode lock, and check whether the random number exists in the set. If it does not exist, we upgraded the Read mode lock to the write mode lock, and then added a new key to the set. Using try/finally blocks is a good way to ensure that we can always release the lock.

9. Use SpinWait to construct

In this section, we will learn how to wait for the execution of a thread without involving the kernel-mode construction. In addition, we will introduce the SpinWait structure, which is a hybrid synchronization structure designed to wait for a while in user mode and then switch it to kernel mode to save CUP time. The procedure is as follows:

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 Recipe09 7 {8 class Program 9 {10 static volatile bool isCompleted = false; 11 12 static void UserModeWait () 13 {14 while (! IsCompleted) 15 {16 Write (". "); 17} 18 WriteLine (); 19 WriteLine (" Waiting is complete "); 20} 21 22 static void HybridSpinWait () 23 {24 // support for spin-based wait 25 var w = new SpinWait (); 26 while (! IsCompleted) 27 {28 // execute a single spin 29 w. spinOnce (); 30 // get the System. threading. spinWait. whether the next call of the SpinOnce will generate a processor and trigger force context switching 31 WriteLine (w. nextSpinWillYield); 32} 33 WriteLine ("Waiting is complete"); 34} 35 36 static void Main (string [] args) 37 {38 var t1 = new Thread (UserModeWait ); 39 var t2 = new Thread (HybridSpinWait); 40 41 WriteLine ("Running user mode waiting"); 42 t1.Start (); 43 Sleep (20); 44 isCompleted = true; 45 Sleep (TimeSpan. fromSeconds (1); 46 isCompleted = false; 47 WriteLine ("Running hybrid SpinWait construct waiting"); 48 t2.Start (); 49 Sleep (5); 50 isCompleted = true; 51} 52} 53}

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

In the above program, we create a thread to execute a wireless loop for 20 milliseconds until the isCompleted variable is set to true in the main thread. We can set this time to 20-30 seconds, and then open the task manager, we can see that the CPU usage is relatively high.

We use the volatile keyword to declare a static field named "isCompleted. The volatile keyword indicates that a field can be modified by multiple concurrent threads. Fields declared as volatile are not restricted by Compiler Optimization (assuming that they are accessed by a single thread. This ensures that the field displays the latest value at any time.

Then, we use the SpinWait version. In the code of line 29th, we call the "SpinOnce" method of the SpinWait to execute a spin. When the spin of the SpinWait reaches a certain number of times, if necessary, the current thread will give up the underlying time slice and trigger context switching. In this version, if we change the wait time of the 49th-line code to 20 ~ 30 seconds, and then open the task manager, you can find that the CPU usage is relatively low.

So far, I have learned about thread synchronization!

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.