21天从Java转向Go之第十一天——水滴石穿(使用共享变量实现并发)

sync.WaitGroup

  • 类似于信号量

 var wait sync.WaitGroup

wait.Add(1)
wait.Done()
wait.Wait()

互斥锁 sync.Mutex


var mu  sync.Mutex

func test(){

  mu.lock()

  defer mu.unlock()

}

sync.RWMutex 读锁

  • RLock只适用于在临界区域内对共享变量五写操作的情形。

var mu = sync.RWMutex

func test(){

  mu.RLock()

  defer mu.RUnlock()

}

 

延迟初始化 sync.Once

  • Once包含了一个布尔变量和一个互斥量,布尔变量记录初始化是否已经完成,互斥量负责保护这个布尔变量和用户需要初始化的数据结构。Once的唯一方法Do以初始化函数作为它的参数。每次调用Do会先锁定互斥量然后访问里面的布尔变量,第一次调用时,这个布尔变量为假,Do会调用初始化函数后把布尔变量设置为true,后续的调用相当于空操作。指数通过互斥量的同步(把缓存的数据刷到主存),保证对所有goroutine可见。

var once sync.Once
func main() {
   once.Do(func() {
      //初始化工作
   })
}

竟态检测器

  • 把-race命令行参数加到go build 、go run、go test

goroutine与线程

可增长的栈

  • 每个OS线程都有一个固定大小的栈内存(通常2MB),栈内存区域用于保存函数调用期间正在执行或临时暂停的函数的局部变量。

  • 一个goroutine在生命周期开始时只有一个很小的栈,通常为2kb。与OS线程类似,goroutine的栈于保存函数调用期间正在执行或临时暂停的函数的局部变量。但是goroutine的栈大小不是固定的,可以按需变化。最大可以达到1GB。

goroutine的调度

  • OS线程由OS内核来调度,每隔几毫秒,一个硬件时钟中断发到CPU,CPU调用一个叫做调度器的内核函数,来做上下文切换。Go运行时包含一个自己的调度器,使用被称为m:n的调度技术(复用或调度m个goroutine到n个OS线程)。Go调度器与内核调度器的工作类似,但Go调度器只关心单个Go程序的goroutine调度问题。

  • GO调度器不是由硬件时钟来定期触发的,而是由特定的Go语言结构来触发。比如当一个goroutine调用time.Sleep或被通道阻塞或对互斥量操作时,调度器会将这个goroutine设置为休眠模式,并运行其他goroutine直到前一个可重新唤醒为止,不需要切换到内核语境,所以调用一个goroutine比调度一个线程成本低。

  • goroutine没有标识,避免像线程里的局部存储(Java里的ThreadLocal)被滥用。

原文地址:https://www.cnblogs.com/perkins/p/15630619.html