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() 是等待全部的事件完成。
本文链接:http://task.lmcjl.com/news/14391.html