首页 > 其他分享 >《Go 语言并发之道》读书笔记(二)

《Go 语言并发之道》读书笔记(二)

时间:2022-11-16 18:13:13浏览次数:45  
标签:wg func 读书笔记 goroutine 并发 go Go salutation main

今天这篇笔记重点讲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非常廉价

标签:wg,func,读书笔记,goroutine,并发,go,Go,salutation,main
From: https://www.cnblogs.com/dk168/p/16896880.html

相关文章

  • 3.go代理basic
    1.通过代理转换后端服务packagemainimport("encoding/base64""log""net/http""os""os/signal""strings")typewebHandlerstruct{......
  • shell 并发
    #!/bin/bash#设置并发数thread_num=3#创建管道文件FIFO=/tmp/$$-FIFOmkfifo$FIFO#使用句柄打开管道文件exec1000<>$FIFOrm-f$FIFO#向管道里面输入若......
  • Good Blogs
    https://www.luogu.com.cn/blog/virus2017/shuweidp数位DPhttps://www.cnblogs.com/Phoenix41/p/12537234.html组合数学选讲......
  • 4.django-模板
    在django中,模板引擎(DTL)是一种可以让开发者将服务端数据填充到html页面中的完成渲染的技术模板引擎的原理分为以下三步:在项目配置文件中指定保存模板文件的的模板目录,一......
  • docker报错Got permission denied while trying to connect to the Docker daemon soc
    报错内容GotpermissiondeniedwhiletryingtoconnecttotheDockerdaemonsocketatunix:///var/run/docker.sock:Get"http://%2Fvar%2Frun%2Fdocker.sock/v1.......
  • 3.django-视图
    django的视图主要有两种,分别是函数视图和类视图,也就是FBV和CBV,先学习FBV1.请求方式常见的请求方式如下GET:获取数据POST:添加数据DELETE:删除数据PUT:更新数据PATCH局......
  • go语言实现第一个程序-hello,world!
    0.前言工作中一直使用c++编写高并发服务器程序,但c++编写高并非服务器程序时多线程逻辑,锁机制导致的死锁,内存泄漏,崩溃等问题会耗费大量时间和精力。听同事说go语言是专门做......
  • Go语言中字符串的查找方法小结
    这篇文章主要介绍了Go语言中字符串的查找方法小结,示例的main函数都是导入strings包然后使用其中的方法,需要的朋友可以参考下 1.funcContains(s,substrstring)bool这......
  • 1.django简介及安装
    1.简介web应用程序的本质接收并解析HTTP请求处理本次请求,完成业务上的处理构造并返回响应HTTP响应   什么是web框架呢?web框架用于搭建web应用程序,免......
  • 使用googlecode+TortoiseSVN进行版本控制(转)
    1.简介       随着写代码越来越多,做的项目越来越多,我们时常会感到反复修改调试代码是个很费劲的过程,有时写好了一段代码,可以工作了,在此基础上再做修改,发现不能工作......