栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

面试被问go的内存模型,怎么回答

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

面试被问go的内存模型,怎么回答

文章目录
  • 前言
  • 一、什么是go的内存模型?
  • 二、为什么需要这些条件?
  • 三、Happen Before
            • init函数
            • 创建/销毁Goroutine
            • channel
            • 锁
            • sync.Once
  • 总结


前言

golang内存模型是什么:

golang面试经常被问到内存模型,不知道怎回答。不知道面试官考察什么。今天就带你来了解golang内存模型


一、什么是go的内存模型?

指定了一系列条件,在这些条件下,可以保证在协程中对变量的读取操作可以观察到其他协程对同一变量写操作的结果,这就是go内存模型。

二、为什么需要这些条件?

因为编译器无法保证指令执行顺序与程序书写顺序一致。

如下示例:

package main
func main() {
    i := 5
    go func() {
        i = 10
    }()
    for j := 0;j<100;j++ {
        fmt.Println(i)
    }
}

多运行几次,会发现结果可能不同,但可以发现,部分Println打印的i变量依然是5,说明未能立观察到协程对i变量的写操作。

所以我们为了保证协程间的变量读取的可观察,就需要建立Happen Before关系的同步事件。也就是内存模型设定的条件。

三、Happen Before

以下几种方式,可以建立Happen Before关系的同步事件:

1.init函数
2.创建/销毁goroutine
3.channel
4.锁
5.Once

init函数

包a引入包b,那么包b的init就会happen before 包a的init函数。如下:

// a.go
package a
import "b"
func init() {
    fmt.Println("a")
    b.DoSomething()
}
// b.go
package b
func init() {
    fmt.Println("b")
}
func DoSomething() {
    fmt.Println("test")
}

输出如下:

b
a
test
创建/销毁Goroutine

创建goroutine happen before goroutine执行
goroutine执行 happen before goroutine销毁

channel

对于无缓冲的channel,recv操作happen before send

package main
import "fmt"
import "time"
func main() {
    i := make(chan int)
    go func() {
        fmt.Println("send")
        i <- 10
    }()
    go func() {
         fmt.Println("recv")
        fmt.Println(<-i)
    }()
   time.Sleep(time.Second)
}

输出结果:

send
recv
10

关闭channel的操作 happen before 接受到0值。
对于容量为m的channel,第n次recv是happen before 第n+m次send

package main

import "fmt"
import "time"
func main() {
    ch := make(chan int, 5)
    go func() {
        for i:=0;i<10;i++ {
            time.Sleep(100)
            fmt.Println("send", i)
            ch <- i
        }
        close(ch)
        
    }()
    go func() {
        for i := range ch {
             time.Sleep(100)
             fmt.Println("receive", i)
        }
    }()
   time.Sleep(time.Second * 2)
}

输出结果发现,对于长度为5的channel,第1次接收比6次发送要先执行。

锁

对于任意的sync.Mutex或者sync.RWMutex,n 对于sync.RWMutex,第n次Unlockhappen before第n次RLock,而第n次RUnlock 又是happen before第n+1次Lock

sync.Once

对于fn()的单个调用会happen before所有的once.Do(fn)返回之前发生

package main
import "fmt"
import "sync"
func main() {
	var o sync.Once
    i := 5
    for j := 0;j<3;j++ {
		o.Do(func() {
			i += 1
		})
		fmt.Println(i)
    }
}

执行结果:

6
6
6
总结

虽然这些都是go开发中一般都知道的常识,但是我们还是需要了解,为什么会这样,为什么需要这样?
在面试过程中问go的内存模型,就只要抓住happen before来回答就好

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1021292.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号