In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-02-23 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Database >
Share
Shulou(Shulou.com)05/31 Report--
This article mainly introduces "the usage of Go mutex lock Mutex and read-write lock RWMutex". In daily operation, I believe many people have doubts about the usage of GE language mutex lock Mutex and read-write lock RWMutex. 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 doubts about the usage of Go mutex lock Mutex and read-write lock RWMutex! Next, please follow the editor to study!
Sync.Mutex
The sync.Mutex type is used in Go to implement mutex (exclusive locks, mutexes). In the sync/mutex.go file of the source code, there are the following definitions:
/ / A Mutex is a mutual exclusion lock. / / The zero value for a Mutex is an unlocked mutex. / A Mutex must not be copied after first use. Type Mutex struct {state int32 sema uint32}
There's nothing extraordinary about it. Everything related to mutex is done through two sync.Mutex type methods, sync.Lock () and sync.Unlock (), which are used to acquire the sync.Mutex lock and the latter to release the sync.Mutex lock. Once sync.Mutex is locked, other Lock () operations can no longer acquire its lock, and only after releasing the lock through Unlock () can it continue to acquire the lock through Lock ().
That is, existing locks cause other goroutine requesting Lock () operations to be blocked and unblocked only when Unlock () is used.
It should also be noted that sync.Mutex does not distinguish between read-write locks, and blocking occurs only between Lock () and Lock (). If Lock () in one place does not Lock () in another, but directly modifies or accesses shared data, this is allowed for the sync.Mutex type, because mutex is not associated with goroutine. If you want to distinguish between read and write locks, you can use the sync.RWMutex type, as described later.
The code segment between Lock () and Unlock () is called the resource critical section (critical section). The code in this interval is strictly protected by Lock () and is thread-safe. Only one goroutine can execute the code in this interval at any one point in time.
The following is an example of using sync.Mutex, which will be analyzed in great detail later.
Package main import ("fmt"sync"time") / / shared variable var (m sync.Mutex v1 int) / / modify shared variable / / the part of the code between Lock () and Unlock () is the critical section func change (I int) {m.Lock () time.Sleep (time.Second) v1 = v1 + 1 if v1 = 0 {v1 = v1-10 roomi} m.Unlock ()} / / access The part of the code between Lock () and Unlock () is the critical section func read () int {m.Lock () a: = v1 m.Unlock () return a} func main () {var numGR = 21 var wg sync.WaitGroup fmt.Printf ("% d") Read () / / Loop to create numGR goroutine / / each goroutine executes change (), read () / / each change () and read () holds the lock for I: = 0 I
< numGR; i++ { wg.Add(1) go func(i int) { defer wg.Done() change(i) fmt.Printf(" ->% d ", read ()} (I)} wg.Wait ()}
Results of the first execution:
20-> 1-> 2-> 3-> 4-> 5-> 6-> 7-> 8-> 9->-99-> 98->-97-> 96->-95->-94->-93->-92->-91->-260->-259
Result of the second execution: note that there is a number between-74 and-72
20-> 1-> 2-> 3-> 4-> 5-> 6-> 7-> 8-> 9-80->-79-> 78-> 77->-76->-75->-74->-72->-71->-230->-229->-229->
In the above example, both change () and read () apply for locks and release them when they are ready to finish executing the function. How they modify and access the data is not explained in this article. What needs to be explained in detail is the for loop part of main ().
In the for loop, new goroutine (a total of 21) is constantly activated to execute anonymous functions, and change () and read () are executed in each anonymous function, which means that each goroutine applies for locks twice and releases locks twice, and there is no Sleep delay in the for loop. These 21 goroutine are activated almost at the same time.
However, since both change () and read () apply for locks, the 42 critical section,Lock () to be executed by each of the 21 goroutine ensures that only one of the goroutine can access one of the critical section at some point in time. When a critical section is released, other Lock () will compete for mutexes, known as race condition. Because of the competition, the order in which the 42 critical section is accessed is random, and there is no guarantee which critical section will be accessed first.
For the first nine dispatched goroutine, no matter which goroutine acquires the critical section in the nine change (I), it only adds one to the shared variable v1. But when the 10th goroutine is scheduled, because v1 plus 1 gets 10, it satisfies the if condition and will execute v1 = v1-igoroutine 10, but this I may be any value between 0 and numGR (because the scheduling order of concurrent goroutine cannot be guaranteed). This makes the value of v1 random from the 10th goroutine. However, in the process of scheduling from the 10th to the 19th goroutine, only one is added to the shared variable v1, these values can be inferred from the 10th number, and then random again to the 20th goroutine. And so on.
In addition, read () in each goroutine also participates in lock competition, so there is no guarantee that each change (I) will be executed to read (). Maybe after the execution of change () in goroutine 1, it will jump to change () in goroutine 3, so that read () in goroutine 1 cannot read the v1 value modified by goroutine 1, but accesses the modified value in other goroutine. Therefore, there is a data leap in the result of the previous second execution. It's just that there's a good chance that read () will be executed immediately after change (), so most of the time the output data is continuous.
All in all, Mutex ensures the security of every critical section, and only one goroutine accesses this part at some point, but it also gives rise to randomness.
If you forget Unlock () after Lock (), it will permanently block and deadlock will occur. If
Data types suitable for sync.Mutex
In fact, it is also unreasonable to use sync.Mutex and Lock () and Unlock () to protect shared variables of built-in types, because they themselves do not contain the Mutex attribute. The really reasonable shared variables are those struct types that contain the Mutex attribute. For example: type mytype struct {
M sync.Mutex var int} x: = new (mytype)
At this point, as long as you want to protect the var variable, first x.m.Lock (), and then x.m.Unlock () after var. This ensures that the var field variables in x must be protected.
Sync.RWMutex
The read-write mutex rwmutex is implemented using the sync.RWMutex type in Go. In the sync/rwmutex.go file of the source code, there are the following definitions:
/ / A RWMutex is a reader/writer mutual exclusion lock.// The lock can be held by an arbitrary number of readers or a single writer.// The zero value for a RWMutex is an unlocked mutex.//// A RWMutex must not be copied after first use.//// If a goroutine holds a RWMutex for reading and another goroutine might// call Lock, no goroutine should expect to be able to acquire a read lock / / until the initial read lock is released. In particular, this prohibits// recursive read locking. This is to ensure that the lock eventually becomes// available; a blocked Lock call excludes new readers from acquiring the// lock. Type RWMutex struct {w Mutex / / held if there are pending writers writerSem uint32 / / semaphore waiting for read lock release semaphore readerSem uint32 / / read lock waiting for write lock release semaphore readerCount int32 / / how many write lock applications readerWait int32 / / how many read locks have been released}
The comments and source code above illustrate a few points:
RWMutex is based on Mutex, adding read and write semaphores to Mutex, and using the number of read locks similar to reference counting.
The read lock is compatible with the read lock, the read lock and the write lock are mutually exclusive, and the write lock and the write lock are mutually exclusive. Only after the lock is released can you continue to apply for mutually exclusive locks.
:
You can apply for multiple read locks at the same time
When there is a read lock, applying for a write lock will block, and when there is a write lock, applying for a read lock will block
As long as there is a write lock, subsequent applications for read lock and write lock will be blocked.
There are several ways to lock and unlock this type:
Func (rw * RWMutex) Lock () func (rw * RWMutex) RLock () func (rw * RWMutex) RLocker () Locker func (rw * RWMutex) RUnlock () func (rw * RWMutex) Unlock ()
Where:
Lock () and Unlock () are used to apply for and release write locks
RLock () and RUnlock () are used to apply for and release read locks once RUnlock () operation only subtracts the number of read locks by 1, that is, reduces the reference count of read locks once
If no write lock exists, Unlock () throws panic, and if there is no read lock, RUnlock () throws panic
RLocker () is used to return a Locker interface that implements Lock () and Unlock () methods
In addition, neither Mutex nor RWMutex is associated with goroutine, which means that their lock application behavior can be operated in one goroutine and lock release behavior in another goroutine.
Because both RLock () and Lock () guarantee that the data will not be modified by other goroutine, the code area between RLock () and RUnlock () and between Lock () and Unlock () is critical section.
The following is an example in which both Mutex and RWMutex,RWMutex are used for reading and writing, and Mutex is used for reading only.
Package main import ("fmt"os"sync"time") var Password = secret {password: "myPassword"} type secret struct {RWM sync.RWMutex M sync.Mutex password string} / / write func Change via rwmutex (c * secret Pass string) {c.RWM.Lock () fmt.Println ("Change with rwmutex lock") time.Sleep (3 * time.Second) c.password = pass c.RWM.Unlock ()} / / read func rwMutexShow (c * secret) string {c.RWM.RLock () fmt.Println ("show with rwmutex", time.Now (). Second () time.Sleep (1 * time.Second) defer c.RWM.RUnlock () return c.password} / / through mutex The only difference between func mutexShow (c * secret) string {c.M.Lock () fmt.Println ("show with mutex:", time.Now (). Second ()) time.Sleep (1 * time.Second) defer c.M.Unlock () return c.password} func main () {/ / defines a function used later to override (rewrite) var show = func (c * secret) string {return "} / / through variable assignment Select and rewrite the showFunc function if len (os.Args)! = 2 {fmt.Println ("Using sync.RWMutex!", time.Now (). Second ()) show = rwMutexShow} else {fmt.Println ("Using sync.Mutex!", time.Now (). Second ()) show = mutexShow} var wg sync.WaitGroup / / activate 5 goroutine, each goroutine views / / depending on the selected function, showFunc () locks in different ways for I: = 0 I < 5; iTunes + {wg.Add (1) go func () {defer wg.Done () fmt.Println ("Go Pass:", show (& Password), time.Now (). Second ()} ()} / / activate a goroutine go func () {wg.Add (1) defer wg.Done () Change (& Password, "123456")} () / / block until all wg.Done wg.Wait ()}
The Change () function applies for a write lock, modifies the data after sleeping for 3 seconds, and then releases the write lock.
The rwMutexShow () function applies for the read lock, takes the data after a second of sleep, and releases the read lock. Notice that the print and return in rwMutexShow () are one second apart.
The mutexShow () function applies for a Mutex lock, which has nothing to do with RWMutex. The only difference from rwMutexShow () is that the lock applied for is different.
In main (), you first decide which show () to run based on the number of command line arguments. The reason why we can assign values according to function variables is that we first define a show () function whose signature is the same as that of rwMutexShow () and mutexShow (), so we can assign values to each other.
Five goroutine are activated in the for loop to run concurrently, and after for instantly activates five goroutine, continuing to execute the main () code activates another goroutine that is used to apply for a write lock. The execution order of these six goroutine is random.
If the function selected by show is rwMutexShow (), the RLock () lock that the five goroutine will apply for conflicts with the write lock, but the five RLock () are compatible. Therefore, as long as the goroutine of the write lock is scheduled at some point in time, the remaining read lock goroutine will begin to block for 3 seconds.
In addition, there is a rule that is not strictly accurate, but can be guaranteed in theory: when the modification data is over, each remaining goroutine applies for a read lock, because immediately after the application, the print output is output, and then sleep for 1 second, but 1 second is enough for all the remaining goroutine to apply for the read lock, so that the show with rwmutex output is connected, and the output Go Pass: 123456 is connected again.
The result of one time is as follows:
Using sync.RWMutex! 58 show with rwmutex 58 Change with rwmutex lock Go Pass: myPassword 59 show with rwmutex 2 show with rwmutex 2 show with rwmutex 2 show with rwmutex 2 Go Pass: 123456 3 Go Pass: 123456 3
If the function selected by show is mutexShow (), read and write data do not conflict with each other, but read and read conflict (because Mutex's Lock () is mutually exclusive).
The result of one time is as follows:
Using sync.Mutex! 30 Change with rwmutex lock show with mutex: 30 Go Pass: myPassword 31 show with mutex: 31 Go Pass: myPassword 32 show with mutex: 32 Go Pass: 123456 33 show with mutex: 33 show with mutex: 34 Go Pass: 123456 34 Go Pass: 123456 35
Mutex or RWMutex?
Neither Mutex nor RWMutex is associated with goroutine, but RWMutex is obviously more suitable for scenarios with more reads and less writes. For read performance only, RWMutex is higher than Mutex because multiple reads of rwmutex can coexist.
At this point, the study on the use of Go mutex Mutex and read-write lock RWMutex 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.