package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
go func() {
fmt.Println("C语言中文网")
mu.Lock()
}()
mu.Unlock()
}
由于 mu.Lock() 和 mu.Unlock() 并不在同一个 Goroutine 中,所以也就不满足顺序一致性内存模型。同时它们也没有其他的同步事件可以参考,也就是说这两件事是可以并发的。
package main
import (
"fmt"
"sync"
)
func main() {
var mu sync.Mutex
mu.Lock()
go func() {
fmt.Println("C语言中文网")
mu.Unlock()
}()
mu.Lock()
}
修复的方式是在 main() 函数所在线程中执行两次 mu.Lock(),当第二次加锁时会因为锁已经被占用(不是递归锁)而阻塞,main() 函数的阻塞状态驱动后台线程继续向前执行。
package main
import (
"fmt"
)
func main() {
done := make(chan int)
go func() {
fmt.Println("C语言中文网")
<-done
}()
done <- 1
}
根据Go语言内存模型规范,对于从无缓存通道进行的接收,发生在对该通道进行的发送完成之前。因此,后台线程<-done 接收操作完成之后,main 线程的done <- 1 发送操作才可能完成(从而退出 main、退出程序),而此时打印工作已经完成了。
package main
import (
"fmt"
)
func main() {
done := make(chan int, 1) // 带缓存通道
go func() {
fmt.Println("C语言中文网")
done <- 1
}()
<-done
}
对于带缓存的通道,对通道的第 K 个接收完成操作发生在第 K+C 个发送操作完成之前,其中 C 是通道的缓存大小。虽然通道是带缓存的,但是 main 线程接收完成是在后台线程发送开始但还未完成的时刻,此时打印工作也是已经完成的。
package main
import (
"fmt"
)
func main() {
done := make(chan int, 10) // 带10个缓存
// 开N个后台打印线程
for i := 0; i < cap(done); i++ {
go func() {
fmt.Println("C语言中文网")
done <- 1
}()
}
// 等待N个后台线程完成
for i := 0; i < cap(done); i++ {
<-done
}
}
对于这种要等待 N 个线程完成后再进行下一步的同步操作有一个简单的做法,就是使用 sync.WaitGroup 来等待一组事件:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
// 开N个后台打印线程
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
fmt.Println("C语言中文网")
wg.Done()
}()
}
// 等待N个后台线程完成
wg.Wait()
}
其中 wg.Add(1) 用于增加等待事件的个数,必须确保在后台线程启动之前执行(如果放到后台线程之中执行则不能保证被正常执行到)。当后台线程完成打印工作之后,调用 wg.Done() 表示完成一个事件,main() 函数的 wg.Wait() 是等待全部的事件完成。
版权说明:Copyright © 广州松河信息科技有限公司 2005-2025 版权所有 粤ICP备16019765号
广州松河信息科技有限公司 版权所有