1. package main
    2. import (
    3. "fmt"
    4. "sync"
    5. "time"
    6. )
    7. /*
    8. 1、定义:
    9. go 中的读写锁采取的是写优先的设计,如果已经有一个 writer 在等待请求锁的话,它会阻止新来的请求锁的 reader 获取到锁,所以优先保障 writer。
    10. 当然,如果有一些 reader 已经请求了锁的话,新请求的 writer 也会等待已经存在的 reader 都释放锁之后才能获取。
    11. 2、原理:
    12. RWMutex 包含一个 Mutex,以及四个辅助字段 writerSem、readerSem、readerCount 和 readerWait:
    13. readerCount 是负数:writer持有锁,将后来的reader阻塞休眠
    14. readerCount 等于0:reader都已经释放了锁,会唤醒writer,让write持有锁
    15. readerCount 不是0:将readerCount 的值保存到readerWait,writer进入阻塞状态
    16. 3、读写锁易错
    17. 重入、递归造成死锁
    18. 写锁等待活跃的reader,活跃的reader等待新来的reader
    19. 未成对出现
    20. 4、锁容易出错
    21. 互斥: 至少一个资源是被排他性独享的,其他线程必须处于等待状态,直到资源被释放。
    22. 持有和等待:goroutine 持有一个资源,并且还在请求其它 goroutine 持有的资源,也就是咱们常说的“吃着碗里,看着锅里”的意思。
    23. 不可剥夺:资源只能由持有它的 goroutine 来释放。
    24. 环路等待:一般来说,存在一组等待进程,P={P1,P2,…,PN},P1 等待 P2 持有的资源,P2 等待 P3 持有的资源,依此类推,最后是 PN 等待 P1 持有的资源,这就形成了一个环路等待的死结。
    25. */
    26. func main() {
    27. var counter Counter
    28. for i := 0; i < 10; i++ { // 10个reader
    29. go func() {
    30. for {
    31. fmt.Println(counter.Count()) // 计数器读操作
    32. time.Sleep(time.Millisecond)
    33. }
    34. }()
    35. }
    36. for { // 一个writer
    37. counter.Incr() // 计数器写操作
    38. time.Sleep(time.Second)
    39. }
    40. }
    41. // 一个线程安全的计数器
    42. type Counter struct {
    43. sync.RWMutex
    44. count uint64
    45. }
    46. // 使用写锁保护
    47. func (c *Counter) Incr() {
    48. c.Lock()
    49. c.count++
    50. c.Unlock()
    51. }
    52. // 使用读锁保护
    53. func (c *Counter) Count() uint64 {
    54. c.RLock()
    55. defer c.RUnlock()
    56. return c.count
    57. }