互斥锁
使用sync.Mutex可以实现“临界区”。
进入临界区之前尝试sync.Mutex.Lock(),如果没有被其他协程锁住,当前协程将获得一个互斥锁。如果被其他协程先锁住,这个操作将会被阻塞直到其他的协程释放了这个互斥锁。
执行完临界区代码之后需要调用sync.Mutex.Unlock()释放互斥锁。
package main
import (
"fmt"
"sync"
"bufio"
"os"
"time"
)
func main() {
lock := new(sync.Mutex)
go test(lock, 1)
go test(lock, 2)
go test(lock, 3)
go test(lock, 4)
go test(lock, 5)
go test(lock, 6)
go test(lock, 7)
go test(lock, 8)
bufio.NewReader(os.Stdin).ReadString('\n')
}
func test(lock *sync.Mutex, index int) {
defer lock.Unlock()
lock.Lock()
fmt.Printf("test start %d\n", index)
time.Sleep(5 * time.Second)
fmt.Printf("test end %d\n", index)
}
test start 4
test end 4
test start 2
test end 2
test start 1
test end 1
test start 6
test end 6
test start 5
test end 5
test start 3
test end 3
test start 7
test end 7
test start 8
test end 8
如果取消互斥锁,即将Lock()和Unlock()注释掉,结果为
test start 3
test start 5
test start 4
test start 8
test start 7
test start 1
test start 2
test start 6
test end 6
test end 3
test end 5
test end 4
test end 8
test end 7
test end 1
test end 2
读写锁
package main
import (
"fmt"
"sync"
"bufio"
"os"
"time"
)
var testCount = 0
func main() {
lock := new(sync.RWMutex)
go test_read(lock, 1)
go test_write(lock, 1)
go test_read(lock, 2)
go test_write(lock, 2)
go test_read(lock, 3)
go test_write(lock, 3)
go test_read(lock, 4)
go test_write(lock, 4)
go test_read(lock, 5)
go test_write(lock, 5)
go test_read(lock, 6)
go test_write(lock, 6)
bufio.NewReader(os.Stdin).ReadString('\n')
}
func test_read(lock *sync.RWMutex, index int) {
defer lock.RUnlock()
lock.RLock()
fmt.Printf("test_read start %d\n", index)
time.Sleep(4 * time.Second)
fmt.Printf("read value is %d\n", testCount)
fmt.Printf("test_read end %d\n", index)
}
func test_write(lock *sync.RWMutex, index int) {
defer lock.Unlock()
lock.Lock()
fmt.Printf("test_write start %d\n", index)
time.Sleep(2 * time.Second)
testCount += 1
fmt.Printf("write value is %d\n", testCount)
fmt.Printf("test_write end %d\n", index)
}
test_read start 3
read value is 0
test_read end 3
test_write start 4
write value is 1
test_write end 4
test_read start 4
test_read start 6
test_read start 1
test_read start 2
test_read start 5
read value is 1
test_read end 5
read value is 1
test_read end 4
read value is 1
test_read end 6
read value is 1
test_read end 1
read value is 1
test_read end 2
test_write start 3
write value is 2
test_write end 3
test_write start 1
write value is 3
test_write end 1
test_write start 2
write value is 4
test_write end 2
test_write start 5
write value is 5
test_write end 5
test_write start 6
write value is 6
test_write end 6
对结果稍加分析可以看出,执行顺序是这样的:
读3,写4等待
写4,读4、6、1、2、5等待
读5、4、6、1、2,写3等待
写3,写1等待
写1,写2等待
写2,写5等待
写5,写6等待
写6
总结:读的时候,可读不可写!写的时候,不可读不可写!
构造锁
package main
import (
"fmt"
"sync"
"time"
"bufio"
"os"
)
var testCount = 0
func main() {
once_lock := new(sync.Once)
go go_test(once_lock, 1)
go go_test(once_lock, 2)
go go_test(once_lock, 3)
go go_test(once_lock, 4)
bufio.NewReader(os.Stdin).ReadString('\n')
}
func go_test(once *sync.Once, index int) {
fmt.Printf("go_test %d start\n", index)
once.Do(test_one)
fmt.Printf("go_test %d end\n", index)
}
func test_one() {
fmt.Printf("------------test_one start\n")
time.Sleep(5 * time.Second)
fmt.Printf("------------test_one end\n")
}
go_test 1 start
------------test_one start
go_test 3 start
go_test 2 start
go_test 4 start
------------test_one end
go_test 1 end
go_test 2 end
go_test 3 end
go_test 4 end
由此可见,sync.Once可以保证传入的函数只做一遍。所以这个锁的这种特别设计常用于代价稍大的初始化。