In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-14 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
This article mainly introduces "how to understand C# multithread safety". In daily operation, I believe many people have doubts about how to understand C# multithread safety. The editor consulted all kinds of materials and sorted out simple and easy-to-use operation methods. I hope it will be helpful to answer the doubt of "how to understand C# multithread safety"! Next, please follow the editor to study!
What is multithreaded safety?
If the results of single-thread and multi-thread execution of a program are inconsistent, it indicates that there is a problem of multi-thread safety, that is, multi-thread is not safe.
Example of multithread safety 1. Example 1 of multithreading unsafe
If we have a requirement that needs to output 5 threads and the wire program number is named 0-4, we write the code as follows:
Private void btnTask1_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * Thread unsafe example btnTask1_Click*"); for (int I = 0; I
< 5; i++) { Task.Run(() =>{Console.WriteLine ($"[BEGIN] * this is the {I} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); Thread.Sleep (2000) Console.WriteLine ($"[END] * this is the {I} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *);} Console.WriteLine (" [end] * thread unsafe example btnTask1_Click* ");}
Then run the example, as follows:
Through the analysis of the above examples, the conclusions are as follows:
1. In the for loop, the five threads started with the thread program number 5 did not output according to our expected result [0mem1jing2jing3jin4].
two。 After analysis, it is found that because I is the same variable in the for loop, the thread starts asynchronously, and there is a delay. When the thread starts, the for loop has ended, and the value of I is 5, which leads to the inconsistency between the thread number and the expectation.
In order to solve the above problems, we can solve the problem by introducing local variables, that is, one variable is declared in each loop, five times, and if there are five variables, they will not be covered with each other. As follows:
Private void btnTask1_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * Thread unsafe example btnTask1_Click*"); for (int I = 0; I
< 5; i++) { int k = i; Task.Run(() =>{Console.WriteLine ($"[BEGIN] * this is the {k} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); Thread.Sleep (2000) Console.WriteLine ($"[END] * this is the {k} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *);} Console.WriteLine (" [end] * thread unsafe example btnTask1_Click* ");}
Run the optimized example as follows:
By running the example, it is found that local variables can solve the corresponding problems.
two。 Example 2 of multithreading unsafe
Suppose we have a requirement: add 0 to 200 to a list and implement it with multithreading, as follows:
Private void btnTask2_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * Thread unsafe example btnTask1_Click*"); List list = new List (); List tasks = new List (); for (int I = 0; I
< 200; i++) { tasks.Add( Task.Run(() =>{list.Add (I);});} Task.WaitAll (tasks.ToArray ()); string res = string.Join (",", list); Console.WriteLine ($"list length: {list.Count}, list content: {res}") Console.WriteLine ("[end] * Thread unsafe example btnTask1_Click*");}
By running the example, it is as follows:
Through the analysis of the above examples, the conclusions are as follows:
1. If the number of entries in the list is incorrect, it will be less.
two。 The element content of the list is inconsistent with the expected content.
In view of the above problems, can it be solved by using intermediate local variables? Give it a try, the modified code is as follows:
Private void btnTask2_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * Thread unsafe example btnTask1_Click*"); List list = new List (); List tasks = new List (); for (int I = 0; I
< 200; i++) { int k = i; tasks.Add( Task.Run(() =>{list.Add (k);});} Task.WaitAll (tasks.ToArray ()); string res = string.Join (",", list); Console.WriteLine ($"list length: {list.Count}, list content: {res}") Console.WriteLine ("[end] * Thread unsafe example btnTask1_Click*");}
Run the optimization example as follows:
By running the above example, the conclusions are as follows:
1. The list length is still incorrect and will be less than the actual length of a single thread. Note: the multithread list length is not necessarily less than the run-time list length of a single thread, but there is a probability that multiple threads have the probability of writing to one location at the same time.
two。 The contents of the list, using local variables, can solve part of the problem.
It can be concluded that List is not a thread-safe data type.
Locked lock
In view of the unsafe problem of multi-thread, it can be solved by locking, the purpose of locking: at any time, the locking block must allow one thread to access.
Locking principle
Lock is actually a grammatical sugar, and the actual effect is equivalent to Monitor. A memory address reference of the reference object is locked. So the locked object cannot be a value type, it can not be a null, it can only be a reference type.
Standard way to write lock objects: by default, lock objects are private, static, read-only, and reference objects. As follows:
/ define a lock object / private static readonly object obj = new object ()
Then optimize the program, as follows:
Private void btnTask2_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * Thread unsafe example btnTask1_Click*"); List list = new List (); List tasks = new List (); for (int I = 0; I
< 200; i++) { int k = i; tasks.Add( Task.Run(() =>{lock (obj) {list.Add (k);}});} Task.WaitAll (tasks.ToArray ()); string res = string.Join (",", list); Console.WriteLine ($"list length: {list.Count}, list content: {res}") Console.WriteLine ("[end] * Thread unsafe example btnTask1_Click*");}
Run the optimized example as follows:
Through the analysis of the above examples, the conclusions are as follows:
1. After locking, the list becomes safe in multithreading and meets the expected requirements.
two。 However, due to locking, only one thread can enter at a time, and other threads will wait, so multithreading becomes single-threaded.
Why should lock objects use private types?
In standard writing, lock objects are private types to prevent lock objects from being used by other threads and, if used, block each other, as shown below:
Suppose you now have a lock object that is used in TestLock, as follows:
Public class TestLock {public static readonly object Obj = new object (); public void Show () {Console.WriteLine ("[start] * Thread sample Show*"); for (int I = 0; I
< 5; i++) { int k = i; Task.Run(() =>{lock (Obj) {Console.WriteLine ($"[BEGIN] * T* this is the {k} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); Thread.Sleep (2000) Console.WriteLine ($"[END] * T* this is the {k} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *");}}) } Console.WriteLine ("[end] * thread sample Show*");}}
Also used in FrmMain, as follows:
Private void btnTask3_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * thread example btnTask3_Click*"); / / multithreaded TestLock.Show () in class object; / / multithreaded for in main method (int I = 0; I
< 5; i++) { int k = i; Task.Run(() =>{lock (TestLock.Obj) {Console.WriteLine ($"[BEGIN] * M* this is the {k} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); Thread.Sleep (2000) Console.WriteLine ($"[END] * M* this is the {k} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *");}}) } Console.WriteLine ("[end] * thread sample btnTask3_Click*");}
Run the above example as follows:
Through the above example, the conclusions are as follows:
1. T and M are adjacent in pairs, and each code block appears interactively.
two。 Multiple blocks of code, sharing a lock, will block each other. This is why it is not recommended to use the public modifier to avoid being locked improperly.
If you use different lock objects, multiple code blocks can be concurrent [T and M are not paired and do not appear adjacent, but have the internal order of the same code block], the effect is as follows:
Why do lock objects use the static type?
If the object is not of type static, then the lock object is an object property, and different objects are independent of each other, so there will be a concurrency problem when different pass objects call the same method, as shown below:
Modify the TestLock code [remove static], as follows:
Public class TestLock {public readonly object Obj = new object (); public void Show (string name) {Console.WriteLine ("[start] * Thread sample Show-- {0} *", name); for (int I = 0; I
< 5; i++) { int k = i; Task.Run(() =>{lock (Obj) {Console.WriteLine ($"[BEGIN] * T* this is the {k}-{name} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); Thread.Sleep (2000) Console.WriteLine ($"[END] * T* this is {k}-{name} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *");}}) } Console.WriteLine ("[end] * thread example Show-- {0} *", name);}}
Declare two objects and call the Show method, as shown below:
Private void btnTask4_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * thread sample btnTask3_Click*"); TestLock testLock1 = new TestLock (); testLock1.Show ("first"); TestLock testLock2 = new TestLock (); testLock2.Show ("second") Console.WriteLine ("[end] * thread sample btnTask3_Click*");}
The test example is as follows:
Through the above example, the conclusions are as follows:
Non-static locking objects are only carried out within the current object to allow only one thread to enter at a time, but multiple objects are concurrent and independent of each other. Therefore, it is recommended that the lock object be a static object.
What is locked?
In lock mode, the memory reference address is locked, not the value of the locked object. If you change the type of Form's lock object to a string, as follows:
/ define a lock object / private static readonly string obj = "Flower without defect"
At the same time, the lock object of the TestLock class is changed to a string, as follows:
Public class TestLock {private static readonly string obj = "flowers without defects"; public static void Show (string name) {Console.WriteLine ("[start] * thread example Show-- {0} *", name); for (int I = 0; I
< 5; i++) { int k = i; Task.Run(() =>{lock (obj) {Console.WriteLine ($"[BEGIN] * T* this is the {k}-{name} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); Thread.Sleep (2000) Console.WriteLine ($"[END] * T* this is {k}-{name} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *");}}) } Console.WriteLine ("[end] * thread example Show-- {0} *", name);}}
Run the above example, and the result is as follows:
Through the above example, the conclusions are as follows:
1. A string is a special type of lock. If the value of the string is the same, it is considered to be the same lock object and blocks between different objects. Because the string type is meta-shared, there is only one flower in the memory heap.
two。 If it is of other types, it is a different lock object that can be concurrent with each other.
3. Indicates that the memory reference address is locked, not the value of the locked object.
Generic lock object
If TestLock is a generic class, it looks like this:
1 public class TestLock 2 {3 private static readonly object obj = new object (); 4 5 public static void Show (string name) 6 {7 8 Console.WriteLine ("[start] * Thread sample Show-- {0} *", name); 9 10 for (int I = 0; I
< 5; i++)11 {12 int k = i;13 Task.Run(() =>14 {15 lock (obj) 16 {17 Console.WriteLine ($"[BEGIN] * T* this is {k}-{name} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); 18 Thread.Sleep (2000) 19 Console.WriteLine ($"[END] * T* this is {k}-{name} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); 20} 21}) 22} 23 24 Console.WriteLine ("[end] * thread example Show-- {0} *", name); 25} 26}
So when calling, will it block each other? The calling code is as follows:
Private void btnTask5_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * Thread sample btnTask5_Click*"); TestLock.Show ("AA"); TestLock.Show ("BB") Console.WriteLine ("[end] * thread sample btnTask5_Click*");}
Run the above example as follows:
Through the analysis of the above examples, the conclusions are as follows:
1. For generic classes, different type parameters can be concurrent with each other, because generic classes will be compiled into different classes for different type parameters, and the corresponding lock objects will become different reference types.
two。 If the lock object is of string type, it will also block each other, simply because the string is a shared metamodel.
3. Different generics T will be compiled into different copies.
Recursive locking
If you add a lock in a recursive function, will it cause a deadlock? The sample code is as follows:
Private void btnTask6_Click (object sender, EventArgs e) {Console.WriteLine ("[start] * thread example btnTask6_Click*"); this.add (1); Console.WriteLine ("[end] * thread sample btnTask6_Click*");} private int num = 0 Private void add (int index) {this.num++; Task.Run (()) > {lock (obj) {Console.WriteLine ($"[BEGIN] * this is the {num} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); Thread.Sleep (2000) Console.WriteLine ($"[END] * this is the {num} thread, thread ID= {Thread.CurrentThread.ManagedThreadId} *"); if (num < 5) {this.add (index);}});}
Run the above example as follows:
By running the above example, the conclusions are as follows:
Locking in a recursive function results in blocking waiting, but does not cause a deadlock.
At this point, the study on "how to understand the multi-thread safety of C#" is over. I hope to be able to solve your doubts. The collocation of theory and practice can better help you learn, go and try it! If you want to continue to learn more related knowledge, please continue to follow the website, the editor will continue to work hard to bring you more practical articles!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.