go基础-13.线程安全和sync.map

线程安全

什么是线程安全?

现在有两个协程,同时触发,一个协程对一个全局变量进行100完成++操作,另一个对全局变量—的操作

那么,两个协程结束,最后的值应该是0才对

package main

import (
  "fmt"
  "sync"
)

var num int
var wait sync.WaitGroup

func add() {
  for i := 0; i < 1000000; i++ {
    num++
  }
  wait.Done()
}
func reduce() {
  for i := 0; i < 1000000; i++ {
    num--
  }
  wait.Done()
}

func main() {
  wait.Add(2)
  go add()
  go reduce()
  wait.Wait()
  fmt.Println(num)

}

但是你会发现,这个输出的结果完全无法预测

这是为什么呢?

根本原因是CPU的调度方法为抢占式执行,随机调度

同步锁

那么我们能不能通过给操作加锁来解决这个问题呢

答案是可以的

package main

import (
  "fmt"
  "sync"
)

var num int
var wait  sync.WaitGroup
var lock  sync.Mutex

func add() {
  // 谁先抢到了这把锁,谁就把它锁上,一旦锁上,其他的线程就只能等着
  lock.Lock()
  for i := 0; i < 1000000; i++ {
    num++
  }
  lock.Unlock()
  wait.Done()
}
func reduce() {
  lock.Lock()
  for i := 0; i < 1000000; i++ {
    num--
  }
  lock.Unlock()
  wait.Done()
}

func main() {
  wait.Add(2)
  go add()
  go reduce()
  wait.Wait()
  fmt.Println(num)

}

线程安全下的map

如果我们在一个协程函数下,读写map就会引发一个错误

concurrent map read and map write

希望大家见到这个错误,就能知道,这个就是map的线程安全错误

package main

import (
  "fmt"
  "sync"
  "time"
)

var wait sync.WaitGroup
var mp = map[string]string{}

func reader() {
  for {
    fmt.Println(mp["time"])
  }
  wait.Done()
}
func writer() {
  for {
    mp["time"] = time.Now().Format("15:04:05")
  }
  wait.Done()
}

func main() {
  wait.Add(2)
  go reader()
  go writer()
  wait.Wait()

}

我们不能在并发模式下读写map

如果要这样做

  1. 给读写操作加锁
  2. 使用sync.Map

加锁

package main

import (
  "fmt"
  "sync"
  "time"
)

var wait sync.WaitGroup
var mp = map[string]string{}
var lock sync.Mutex

func reader() {
  for {
    lock.Lock()
    fmt.Println(mp["time"])
    lock.Unlock()
  }
  wait.Done()
}
func writer() {
  for {
    lock.Lock()
    mp["time"] = time.Now().Format("15:04:05")
    lock.Unlock()
  }
  wait.Done()
}

func main() {
  wait.Add(2)
  go reader()
  go writer()
  wait.Wait()
}

sync.Map

package main

import (
  "fmt"
  "sync"
  "time"
)

var wait sync.WaitGroup
var mp = sync.Map{}

func reader() {
  for {

    fmt.Println(mp.Load("time"))
  }
  wait.Done()
}
func writer() {
  for {
    mp.Store("time", time.Now().Format("15:04:05"))
  }
  wait.Done()
}

func main() {
  wait.Add(2)
  go reader()
  go writer()
  wait.Wait()

}

其实看它源码,它的内部也是用了同步锁的

posted @ 2024-09-24 11:08  枫枫知道  阅读(7)  评论(0编辑  收藏  举报