funcmain() { var mutex sync.Mutex fmt.Println("Lock the lock") mutex.Lock() fmt.Println("The lock is locked") channels := make([]chanint, 4) for i := 0; i < 4; i++ { channels[i] = make(chanint) gofunc(i int, c chanint) { fmt.Println("Not lock: ", i) mutex.Lock() fmt.Println("Locked: ", i) time.Sleep(time.Second) fmt.Println("Unlock the lock: ", i) mutex.Unlock() c <- i }(i, channels[i]) } fmt.Println("Waiting") time.Sleep(time.Second) fmt.Println("Unlock the lock") mutex.Unlock() time.Sleep(time.Second)
for _, c := range channels { <-c } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
output:
Lock the lock The lock is locked Waiting Not lock: 3 Not lock: 1 Not lock: 2 Not lock: 0 Unlock the lock Locked: 3 Unlock the lock: 3 Locked: 1 Unlock the lock: 1 Locked: 2 Unlock the lock: 2 Locked: 0 Unlock the lock: 0
在解锁之前加锁会导致死锁
1 2 3 4 5 6 7 8 9 10 11 12 13
package main
import ( "fmt" "sync" )
funcmain() { var mutex sync.Mutex mutex.Lock() fmt.Println("Locked") mutex.Lock() }
程序输出
1 2
Locked fatal error: all goroutines are asleep - deadlock!
funcmain() { var mutex *sync.RWMutex mutex = new(sync.RWMutex) fmt.Println("Lock the lock") mutex.Lock() fmt.Println("The lock is locked")
channels := make([]chanint, 4) for i := 0; i < 4; i++ { channels[i] = make(chanint) gofunc(i int, c chanint) { fmt.Println("Not lock: ", i) mutex.Lock() fmt.Println("Locked: ", i) fmt.Println("Unlock the lock: ", i) mutex.Unlock() c <- i }(i, channels[i]) } fmt.Println("waiting") time.Sleep(time.Second) fmt.Println("Unlock the lock") mutex.Unlock() time.Sleep(time.Second)
for _, c := range channels { <-c } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// output:
Lock the lock The lock is locked waiting Not lock: 0 Not lock: 3 Not lock: 1 Not lock: 2 Unlock the lock Locked: 0 Unlock the lock: 0 Locked: 3 Unlock the lock: 3 Locked: 1 Unlock the lock: 1 Locked: 2 Unlock the lock: 2
funcmain() { var mutex *sync.RWMutex mutex = new(sync.RWMutex) fmt.Println("Lock the lock") mutex.Lock() fmt.Println("The lock is locked")
channels := make([]chanint, 4) for i := 0; i < 4; i++ { channels[i] = make(chanint) gofunc(i int, c chanint) { fmt.Println("Not read lock: ", i) mutex.RLock() fmt.Println("Read Locked: ", i)
time.Sleep(time.Second) fmt.Println("Unlock the read lock: ", i) mutex.RUnlock() c <- i }(i, channels[i]) } time.Sleep(time.Second) fmt.Println("Unlock the lock") mutex.Unlock() time.Sleep(time.Second)
for _, c := range channels { <-c } }
程序输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Lock the lock The lock is locked Not read lock: 0 Not read lock: 3 Not read lock: 1 Not read lock: 2 Unlock the lock Read Locked: 2 Read Locked: 0 Read Locked: 3 Read Locked: 1 Unlock the read lock: 1 Unlock the read lock: 3 Unlock the read lock: 2 Unlock the read lock: 0
Unlock() 使用之前不存在 Lock()
1 2 3 4 5 6 7 8 9 10 11
package main
import ( "sync" )
funcmain() { var rwmutex *sync.RWMutex rwmutex = new(sync.RWMutex) rwmutex.Unlock() }
funcmain() { var wg sync.WaitGroup wg.Add(2) m := sync.Mutex{} c := sync.NewCond(&m) gofunc() { // this go routine wait for changes to the sharedRsc c.L.Lock() for sharedRsc == false { fmt.Println("goroutine1 wait") c.Wait() } fmt.Println("goroutine1", sharedRsc) c.L.Unlock() wg.Done() }()
gofunc() { // this go routine wait for changes to the sharedRsc c.L.Lock() for sharedRsc == false { fmt.Println("goroutine2 wait") c.Wait() } fmt.Println("goroutine2", sharedRsc) c.L.Unlock() wg.Done() }()
fmt.Println("waiting") // this one writes changes to sharedRsc time.Sleep(2 * time.Second) c.L.Lock() fmt.Println("main goroutine ready") sharedRsc = true c.Broadcast() fmt.Println("main goroutine broadcast") c.L.Unlock() wg.Wait() }
Wait 调用的条件检查一定要放在 for 循环中。这是因为当 Boardcast 唤醒时,有可能其他 goroutine 先于当前 goroutine 唤醒并抢到锁,导致轮到当前 goroutine 抢到锁的时候,条件又不再满足了。因此,需要将条件检查放在 for 循环中。
Signal 和 Boardcast 两个唤醒操作不需要加锁。
sync.ErrGroup
Go 团队发布的第一个 goroutines 的管理工具是 sync.WaitGroup,这个工具允许你创建 WaitGroup 去等待一定数量的 goroutines 执行完成。这里有个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
var wg sync.WaitGroup var urls = []string{ "http://www.golang.org/", "http://www.google.com/", "http://www.somestupidname.com/", } for _, url := range urls { // Increment the WaitGroup counter. wg.Add(1) // Launch a goroutine to fetch the URL. gofunc(url string) { // Decrement the counter when the goroutine completes. defer wg.Done() // Fetch the URL. http.Get(url) }(url) } // Wait for all HTTP fetches to complete. wg.Wait()