首页IT科技go语言高并发与微服务实战 下载(《Go 语言并发之道》读书笔记(二))

go语言高并发与微服务实战 下载(《Go 语言并发之道》读书笔记(二))

时间2025-05-05 04:29:20分类IT科技浏览3502
导读:今天这篇笔记重点讲goroutine 首先怎么定义goroutine...

今天这篇笔记重点讲goroutine

首先怎么定义goroutine

很简单           ,在方法前面加上go就可以了

func main() { go sayHello() } func sayHello() { fmt.Println("hello") }

也可以直接这样写, 基于匿名函数

go func() { fmt.Println("hello") }()

go 语言至少有一个main goroutine                 , 另外调用的sayhello goroutine和main goroutine并发执行      ,会在main goroutine退出后退出           ,所以我们上面的代码是有问题的                ,它不会打印出"hello". 因为main goroutine退出了      ,它没有机会执行           。我们需要用到WaitGroup在main goroutine上面等待它      ,如下代码

var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() fmt.Println("hello") }() wg.Wait()

wg.Add(1), 表示会执行一次                , wg.Done就是说执行了一次                 。 wg.Wait()会等待add的一次done.

main goroutine 可以和 goroutine共享相同的地址空间执行           ,如下代码

var wg sync.WaitGroup salutation := "hello" wg.Add(1) go func() { defer wg.Done() salutation = "welcome" }() wg.Wait() fmt.Println(salutation)

会打印出“welcome           ”, salutation变量在goroutine中被改变后      ,也会在main goroutine中生效      。

我们来看下面的代码会输出什么

var wg sync.WaitGroup for _, salutation := range []string{"hello", "greetings", "good day"} { wg.Add(1) go func() { defer wg.Done() mt.Println(salutation) }() } wg.Wait()

它会打印三个good day, 为什么呢? 作者的解释是在goroutine开始之前循环有很高的概率会退出                 ,salutation的值不在范围之内           , go 语言运行时会足够小心的将变量salutation值得引用仍然保留,由内存转移到堆     。 我不是特别明白作者得解释                 ,我自己理解下                 ,感觉是for循环是main goroutine, 它执行for很快           ,调用goroutine得时候它已经循环完了                 ,所以就拿到最后得值了                 。

这个我们想打印三个不同得值      ,使用下面得代码 var wg sync.WaitGroup for _, salutation := range []string{"hello", "greetings", "good day"} { wg.Add(1) go func(value string) { defer wg.Done() fmt.Println(value) }(salutation) } wg.Wait()

这样三个goroutine使用的是各自的副本           。

goroutine的开销是什么样的呢?

一个goroutine占多少内存? 作者书上说大概是2.817KB, 我自己实验了下我的机器上是9.072KB           ,而OS线程的一般会是2M, 差距还是有些大                ,所以我们说启动百万的goroutine也是很正常的      ,而线程一般几十个就不错了     。 下面是示例代码

func main() { memConsumed := func() uint64 { runtime.GC() var s runtime.MemStats runtime.ReadMemStats(&s) return s.Sys } var c <-chan interface{} var wg sync.WaitGroup noop := func() { wg.Done() <-c } const numGoroutines = 5e4 wg.Add(numGoroutines) before := memConsumed() for i := numGoroutines; i > 0; i-- { go noop() } after := memConsumed() fmt.Printf("%.3fkb", float64(after-before)/numGoroutines/1000) }

numGoroutines是我们想创建的goroutine数量

noop 这个方法一直等channel里面的value      ,它不会退出                ,创建了的goroutine一直在内存中                 。

memConsumed 方法统计当前的内存           , 我们在开始的时候统计下      ,在结束的时候统计下                 ,相减后就得到消耗的内存           。

作者还列举了goroutine的上下文切换耗时225ns, 而线程切换1467ns, 相差也比较大           , 最后

作者得出的结论是使用goroutine非常廉价

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
怎么采集其他网站内容(怎样采集需要登陆的网页内容-登录查看内容采集软件)