[Go] cond condition variable
Before you look at cond, you can look at the producer consumer model
For example, there are two processes a and B, which share a fixed size buffer. Process a generates data and puts it into the buffer. Process B takes data from the buffer for calculation. In fact, this is a producer and consumer model. A is equivalent to producer and B is equivalent to consumer
Classic concurrent synchronization mode: producer consumer design mode Zhihu (zhihu.com)
So how to implement it in go language?
The first idea is to use two go co processes for cycle production and consumption respectively, but concurrency problems may occur, so it should be locked.
//package import type Product struct{ len int cap int lock sync.RWMutex } func main() { data := Product{ len: 0,//Current inventory 0 cap: 10,//Capacity 10 lock: sync.RWMutex{},//Read write lock } go func() { //Production once in 0.1 second for{ data.lock.Lock() if data.len==data.cap {data.lock.Unlock();continue}//If the warehouse is full, stop production and wait for consumption (continue cycle judgment) data.len++ log.Printf("Production to%d piece",data.len) data.lock.Unlock() time.Sleep(100 * time.Millisecond)//Simulated production commodity time } }() go func() { //Consume once in 0.5 seconds for{ data.lock.Lock() if data.len == 0{data.lock.Unlock();continue}//If there is no inventory, stop consumption and wait for production (continue cycle judgment) data.len-- log.Printf("Consumption to%d piece",data.len) data.lock.Unlock() time.Sleep(500 * time.Millisecond)//Simulated consumption time } }() time.Sleep(3*time.Second) }
-
Disadvantages: judge whether the production of goods needs to be recycled all the time, wasting resources
Solution 1: when judging that the goods are full, rest for a fixed time
Disadvantages: unable to produce in time during rest time
Solution 2: rest when the goods are full, and start production when the goods are not full
The first solution is to directly add rest time to the judgment of coordination process
//Abbreviated context if data.len==data.cap { data.lock.Unlock() time.Sleep(100 * time.Millisecond)//Add break time continue }
The advantages are that the idling time is reduced compared with the original, and the disadvantages are obvious. It can not be produced during the rest time
The second solution is to use the condition variable cond of go
cond can notify and wake up other cooperation processes, reduce resource consumption and carry out production in time
The first is to create condition variables. cond needs to be used with locks. The creation method is as follows:
type Product struct{ len int cap int lock sync.RWMutex cond *sync.Cond//Conditional variable } func main(){ data := Product{len: 0,cap: 10,lock: sync.RWMutex{},} data.cond=sync.NewCond(&data.lock)//Create a condition variable to match the corresponding lock //...... }
After creating the condition variable, specify the following:
① We need to wait for production when the inventory is 0
② We need producers to wait for consumption when inventory is full
Add cond. To these two judgments Wait() function, which means to enter the rest and wait to be awakened. After production or consumption, add cond Signal() (indicates waking up a blocked other process):
func main(){ //...... go func() { //Production once in 0.1 second for{ data.lock.Lock() if data.len==data.cap { data.cond.Wait()//wait() will continue to execute downward after it is awakened by signal() } data.len++ log.Printf("Production to%d piece",data.len) data.lock.Unlock() //End of production if data.len > 0{ data.cond.Signal() } //Simulated production commodity time time.Sleep(100 * time.Millisecond) } }() go func() { //Consume once in 0.5 seconds for{ data.lock.Lock() if data.len == 0{ data.cond.Wait() } data.len-- log.Printf("Consumption to%d piece",data.len) data.lock.Unlock() //End of consumption data.cond.Signal() //Simulated consumption time time.Sleep(500 * time.Millisecond) } }() //...... }
In this way, solution 2 is completed. The complete code is as follows:
package main import ( "log" "sync" "time" ) type Product struct{ len int cap int lock sync.RWMutex cond *sync.Cond } func main() { data := Product{ len: 0,//Current inventory 0 cap: 10,//Capacity 10 lock: sync.RWMutex{}, } data.cond=sync.NewCond(&data.lock) go func() { // Question: judge whether the production of goods needs to be recycled all the time, wasting resources // Solution 1: when judging that the goods are full, rest for a fixed time // Solution 2: rest when the goods are full, and start production when the goods are not full //Production once in 0.1 second for{ data.lock.Lock() if data.len==data.cap { data.cond.Wait() } data.len++ log.Printf("Production to%d piece",data.len) data.lock.Unlock() data.cond.Signal() //Simulated production commodity time time.Sleep(100 * time.Millisecond) } }() go func() { //Consume once in 0.5 seconds for{ data.lock.Lock() if data.len == 0{ data.cond.Wait() } data.len-- log.Printf("Consumption to%d piece",data.len) data.lock.Unlock() data.cond.Signal() //Simulated consumption time time.Sleep(500 * time.Millisecond) } }() time.Sleep(5*time.Second) }
Let's briefly understand the wait method:
The basic process of wait method is as follows- Unlock (release the lock to prevent other processes from being unable to obtain the lock)
- Waiting for notification
- Lock after being notified and continue to execute
As for the signal and broadcast methods, there is no upper unlock operation, but only notification.