Post

Golang 锁

sync 包中 Locker 提供互斥锁(Mutex)和读写锁(RWMutex

  • 为什么要锁

    go run test1.go |sort|uniq |wc -l 会发现下面代码明明是递增 但是结果会出现最后可能不是999

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
      package main
    
      import (
        "fmt"
        "time"
      )
    
      func main() {
        var a = 0
        for i := 0; i < 999; i++ {
          go func(idx int) {
            a += 1
            fmt.Println(a)
          }(i)
        }
        time.Sleep(time.Second)
      }
    
  • 怎么解决以上问题

    可是使用锁来实现

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    package main
    
    import (
      "fmt"
      "sync"
      "time"
    )
    
    func main() {
      var a = 0
      var lock sync.Mutex
      for i := 0; i < 999; i++ {
        go func(idx int) {
          lock.Lock()
          defer lock.Unlock()
          a += 1
          fmt.Println(a)
        }(i)
      }
      // 等待 1s 结束主程序
      // 确保所有协程执行完
      time.Sleep(time.Second)
    }
    

互斥锁(Mutex

只能有一个线程占用资源,其他线程只能等待

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

import (
  "fmt"
  "sync"
)

var mutex sync.Mutex

func printFunc(str string) {
  mutex.Lock()
  defer mutex.Unlock()
  for _, data := range str {
    fmt.Printf("%c", data)
  }
  fmt.Println()
}
func main() {
  var waitGroup sync.WaitGroup
  waitGroup.Add(2)
  go func() {
    defer waitGroup.Done()
    printFunc("hello")
  }()
  go func() {
    defer waitGroup.Done()
    printFunc("world")
  }()
  waitGroup.Wait()
}

读写锁(RWMutex

读写

  • 写操作的锁定和解锁

  • func (*RWMutex) Lock
  • func (*RWMutex) Unlock

  • 读操作的锁定和解锁
  • func (*RWMutex) Rlock
  • func (*RWMutex) RUnlock

前言

当有一个 goroutine 获得写锁定,其它无论是读锁定还是写锁定都将阻塞直到写解锁;当有一个 goroutine 获得读锁定,其它读锁定仍然可以继续;当有一个或任意多个读锁定,写锁定将等待所有读锁定解锁之后才能够进行写锁定。所以说这里的读锁定(RLock)目的其实是告诉写锁定:有很多人正在读取数据,你给我站一边去,等它们读(读解锁)完你再来写(写锁定)。我们可以将其总结为如下三条:

  1. 同时只能有一个 goroutine能够获得写锁定
  2. 同时可以有任意多个 gorouinte获得读锁定
  3. 同时只能存在写锁定或读锁定(读和写互斥)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    package main
    
    import (
      "fmt"
      "sync"
      "time"
    )
    
    var str = "Hello World"
    var rw sync.RWMutex
    
    func main() {
    
      go func() {
        for i := 0; i < 100; i++ {
          read()
        }
    
      }()
      go func() {
        for i := 0; i < 1; i++ {
          write("write")
        }
      }()
      time.Sleep(time.Second * 2)
    }
    
    // 读
    func read() {
      rw.RLock()
      defer rw.RUnlock()
      fmt.Println("读", str)
    }
    
    // 写
    func write(n string) {
      rw.Lock()
      defer rw.Unlock()
      str += "no HelloWorld " + n
      fmt.Println("写", str)
      for i := 0; i < 100; i++ {
        fmt.Println("在写入请等下")
      }
    }
    
This post is licensed under CC BY 4.0 by the author.