P88 今日内容
go的优势就在于并发
P89内容回顾
go语言的日期要记住,2006-01-02 13:04:05
还有after和before
runtime.caller可以获取到运行时的堆栈信息
接口类型的变量底层分为两部分:动态类和动态值。
反射的应用,可以解析json,xml各种数据解析,还有orm框架。
反射的两个方法:reflact.Typeof得到动态类型的信息,reflact.valueof动态值类型
把字符串解析到student的结构体
unmarshall参数是空接口,可以接收任何类型的值,然后反射去把对应字段解析
P90 ini配置文件解析1
可以试试map类型,读取特定字符串,值存入到相应的字段里
读文件,读出字节类型的数据
ptr就是指针
返回的是一个error类型
格式化输出后返回一个error类型
t是指针类型,elem拿到值,等于先判断是不是指针,再判断是不是结构体
make初始化只针对slice,map,channel,剩下都是new,new是给值类型做初始化的。必须是要一个结构体指针,传一个int类型的指针是不行的
换成结构体指针就没问题了
读文件,ioutil,数据保存到一个变量,然后按照换行符切割。
P91 init配置解析2
配置文件读取出来放到了一个slice里
一行行读数据,遇到注释的就跳过。如果[开头就是段落section,把【】去掉,读取下面的内容
提示这里有语法错误
现在格式没问题,就没有语法错误了
获取字段名,并在相应data里根据反射找对应的结构体,造一个结构体的嵌套,找到mysql对应的结构体,再从对应结构体里找不同的字段
结构体名字接收变量
for循环遍历拿到数据里的值,找到对应的mysql段落还是其他段落
使用指针
如果走到了【】段落名,那就跳出循环,读取该段落的配置字段了,
1.也就是以等号分割这一行,等号左边是key,右边是value。(不规范的要去掉,提示语法错误)
2.根据structname去data里面把对应的嵌套结构体给取出来。
3.遍历嵌套结构体的每一个字段,判断tag是不是等于key。
4.如果是key=tag,给这个字段 赋值
空行跳过
这样就没问题了
根据struct name去data里把对应的字段值取出来
先把key拿出来,切到等于号,key就是address,value就是后面的ip地址。
遍历嵌套结构体字段,判断tag是不是等于key
下面拿到的值也需要稍微改动,等于key就找到了对应的字段
第四步,赋值
把值存起来
找到对应字段就退出
下面判断obj,reflact如果是一个string,就设置成string
这里字符串就都有了
字符串有了,加个int类型,那就涉及到把之前string类型的数字转成int类型
可以使用strconv.parseint,转几进制数,多少位,返回int64和err
10进制数,64位,现在parseint还有问题
解析错误,17行有问题
xxx这里应该找不到字段,就直接跳过了,没有必要赋值
port的值类型错误。
现在就都加载出来了
判断float
添加一个test布尔值
流程:从配置文件读取出,把数据依次赋值到结构体里,利用反射一个字段一个字段去找,根据结构体tag值找到key值,
所有配置文件解析,go语言里都是做map的
也有现成的ini库
P92 strconv标准库介绍
go是强类型语言,类型和类型之间不能互相转换,支持强制转换的前提是支持转换的,int64转int32,数字和字符串类型就不能互相转了。
数字当作ascii码了,拿编码找到了这个字符
拿到字符串的97,直接string(),是用数字做一个utf-8编码了,去找对应符号去了
还有一个方法,strconv.parseint,字符串转int
这样1000这个字符串就转成int64了,前面的10代表10进制
10,0,传0的话就是int类型,但返回值定死了,就是int64
这样就返回int
但是转成int一般不用上面的,Atoi就是字符串转成int
数字转换成字符串Itoa
strconv是把对应的字符传解析成需要用的数据,比如boolstr
P93 并发编程介绍
java做并发,需要os里线程池,包装一下,起一个线程,执行完再关闭,这里os线程运行的时候会遇到上下文切换,一个线程最起码要2M。
go里实现了自己的线程,goroutine,切换的时候是自己切。
并发代表同一段时间 内执行多个任务。
并行代表同一时刻执行多个任务。
go的并发用goroutine实现。等于在cpu阻塞的时候去做其他的事情。类似于线程,但是是属于用户态的线程。
goroutine是由go语言的运行时runtime调度完成的。
channel是在用多个goroutine进行通信的,协调工作用的。
P94 创建goroutine
在调用函数的时候前面加上go 关键字,就能实现并发。
**
程序启动之后会创建一个主goroutine去执行,go关键字代表开启一个单独的goroutine去执行hello函数(任务)**
现在打印一个main,main函数结束,由main函数启动的goroutine也都结束了
会停顿一下
打印多个,打印的时候顺序不一定
P95 sync.WaitGroup
改造成匿名函数,为什么有3个999,打印i这个变量,这个变量是在外面拿的,启动一个go routine的 时候,可能for循环转的够快,已经走了好几个循环了,所以打印的时候出现了多个。
可以在函数执行的时候把参数传进去,这样每个for循环就把值主动 传进去了。说明启动goroutine是要消耗时间和资源的。
**优雅方式等待goroutine去结束,sync包里有一个Waitgroup
**
goroutine对应的函数执行结束了,gocroutine才结束
程序编译好了,每次执行的都一样,赋值进去了,每次执行都是一样的
1970年到现在的纳秒数,这样肯定是不一样的,这样 写的程序的随机数就没问题
struct是值类型
done代表计数器结束了
通常这么写,确保done了
启动10个goroutine
等待计数器减为0就退出,3可能还没来得及输出就退出了
等待时间 长点就可以看到3了
sync.waitgroup可以同步多个goroutine退出才退出
P96 goroutine调度模型GMP
goroutine是用户态的线程 ,是go语言自己实现的类似线程的东西。
goroutine和线程 的区别就是栈不一样 。os线程一般都固定栈的内存是2M,一个goroutine的栈不是固定的,最小2Kb,最大1GB。
所以go语言中可以一次创建十万作用的goroutine也是可以的。
goroutine调度,GMP调度
G:就是goroutine,里面除了存放本goroutine信息外还有与所在ip的绑定等信息。
M:machine 是Go运行时对操作系统内核线程的虚拟,M与内核线程一般是映射关系,一个gorutine最终是要放到M上执行的。
P:管理一组goroutine队列,P里面会存储当前goroutine运行的上下文环境(函数指针,堆栈地址及地址边界),P会对自己管理的goroutine队列做一些调度(比如把占用cpu时间较长的goroutine暂停、运行后续的goroutine等等)当自己的队列消费完了就去全局队列里重取,如果全局队列里也消费完了会去其他P的队列里抢任务。
当io阻塞的时候 ,就会切换。p的个数是通过runtime.GOMAXPROCS设定,最大256,go1.5版本之后默认为物理线程数,在并发量打的时候会则更加一些P和M,但不会太多。
所有都是p调度的,也就是go语言自己调度,比其他语言用操作系统调度,就省很多资源。把m个goroutine放到一个操作系统线程上进行工作。
windows无效,体会不出来,先试个1核
先打印B再打印A
多核可能会出现打印错乱的情况
默认就是cpu的核心数
操作系统启动的是进程,但是真正干活的是线程
操作系统线程核goroutine的关系:
1个操作系统线程对应用户多个goroutine
go程序同时可以有多个操作系统编程。
goroutine核OS线程是多对多关系。
goroutine调度模型,GMP
G是goroutine
M是用于和操作系统线程做映射的。
P是管理goroutine,阻塞的时候进行切换
P97 channel初识
单纯让函数起一个goroutine没有意义,肯定是中间有多个环节进行交互。
现在不是使用共享内存来实现数据通信(这样会数据加锁,并行变串行)而是通过通信,实现共享内存。go语言用通道实现多个goroutine通信的。
goroutine是go语言并发的执行体,channel相当于做链接。channel是一个特殊类型,类似传送带,遵循先入先出
需要指定通道中元素的类型,channel是一个引用类型,需要初始化去使用,分配内存使用。
make,初始化slice,map,channel
带缓冲区和不带缓冲区,满了的话有人接才能放
channel的使用有三种操作,发送,接收,关闭
不管发送还是接收,都是用<小于号
发送是从goroutine到channel,接收是从channel到goroutine
相当于卡在这里了
等于永远放不进去,现在是一个main的一个goroutine
可以再起一个匿名函数试试
使用sync.waitgroup等待goroutine结束
现在就打印出来了
下面的放进去,上面的才能取出来
没有缓冲区的channel
带缓冲区的channel,初始化里,里面最多存一个容量,所以下面就可以放进去
取值
只有1个容量,所以,下面的就卡住了
关闭close掉
箭头代表数据的流向
如何优雅地从通道循环取值
forange也支持从通道取值
P98 channel练习
先造100个数放到通道1,另外的goroutine从通道1取值,每个乘平方,然后把结果放到通道2里。
f1循环放值,f2循环取值
最后读不到数据,会返回一个false,只要不关闭,一直都是true
这个数就算出来了
sys.once只执行一次
once确保某个操作只执行一次,只关闭一次
关闭通道的时候,如果原来有值,读会继续读,之后返回值是false
对一个关闭通道继续取值,是可以取的,只不过会是false
关闭的通道取多少次都是false,bool是false,字符串就是为空
P99 单向通道
单向通道一般用在函数的参数里,限制别人只想发送什么,或接收什么。确保暴露出去的通道只能有一项操作。限制你一个函数里只能往ch2里放进去,不能取出来。
返回的是一个通道,通道类型是时间类型。单向通道,只能从里面去读取。
channel三种操作,接收发送,关闭。
通道状态,nil,非空,空的,满了,没满。
nil没有初始化的时候,接收,发送都是阻塞的,关闭会panic,关闭已经关闭的会panic。
如果主程序一直运行,后面goroutine一直占内存,永远不可能把内存释放,这是goroutine泄露
P100 work_pool练习
goroutine池,work_pool这种模式
造三个goroutine去执行任务
worker是包装了一个任务,id是int,jobs是一个只读的通道,result是只写的通道。
从只读通道取值,算乘2 的值
起三个goroutine,5个任务
id为3的goroutine先起5个任务
开启一个goroutine循环加入job
从job取数,计算合,发送到result
打印数
P101 select介绍
从多个通道去取,叫多路复用。
想要实现每次随机从c1或者c2里取
c1取不到到c2里取,c2取不到到c1里取
select,多个case满足,可以随机选一个
这样每次执行结果都不一样
P102 作业要求
之前的日志是同步写的,可以起一个goroutine,日志放到channel里,一直从channel里拿日志写文件。