I. Misdescription
Error code demo
func main() {
var once sync.Once
once.Do(func() {
DemoTest()
once = sync.Once{}
})
once.Do(func() {
DemoTest()
})
}
func DemoTest() {
fmt.Println("This is a test demo for sync.Once")}Copy the code
Execute code output:
Ii. Error cause analysis
Error: FATAL error: sync: unlock of unlocked mutex In the unlocked code, the UNLOCKED mutex is unlocked.
Learn about the internals of the following sync.once package:
type Once struct {
done uint32
m Mutex
}
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 0 {
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) {
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
Copy the code
The sync.once package contains a Mutex variable that performs lock and unlock operations during Do.
We analyze the assignment of the once variable in the source code:
once.Do(func() {
DemoTest()
once = sync.Once{}
})
Copy the code
Once is an assignment of a value, and the second assignment overrides the first assignment. So, once executes a lock() operation on its internal mutex when entering the Do function. Once is a new non-lock object when performing a secondary assignment, while Do() continues to perform a lock operation on the same address. Defer O.m.lock () before return generates a program error.
Iii. Code modification
The above error is caused by copying the value of the variable of the structure containing the lock. The new variable overwrites the original variable, and the operation is performed at the same address. Therefore, we can assign the variable by pointer, so that the new variable will not overwrite the original variable.
func main() {
var once *sync.Once
once = &sync.Once{}
once.Do(func() {
DemoTest()
once = &sync.Once{}
})
once.Do(func() {
DemoTest()
})
}
Copy the code
Execution Result: