在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
1.切片 (1)切片判断是否为空要使用 len(slice) == 0 来进行判断,不能使用slice == nil来进行判断。 (2)切片追加使用apeend(),切片复制使用copy() func test () { s1 := []int{1,2,3,4,5} s2 := make([]int, 10) fmt.Println(s2); copy(s2, s1) fmt.Println(s2); } (3)切片底层是数组,切片是引用类型变量,指向一个底层数组,如果底层数组长度不够用,GO底层会将底层数组换一个。 (4)切片不提供删除的方法,需要手动处理删除动作。 2.引用类型和值类型 值类型 值类型包括基本数据类型,int,float,bool,string,以及数组和结构体(struct)。 值类型变量声明后,不管是否已经赋值,编译器为其分配内存,此时该值存储于栈上。 引用类型 引用类型包括指针(指针包括基本数据类型的指针地址),slice切片,map ,chan,interface。 变量直接存放的就是一个内存地址值,这个地址值指向的空间存的才是值。所以修改其中一个,另外一个也会修改(同一个内存地址)。 引用类型必须申请内存才可以使用,make()是给引用类型申请内存空间。 3.GO语言不存在指针操作 通过符号“&”和间接访问符号“*"进行操作。 (1)make和new的区别: 在Go语言中: make 被用来分配引用类型的内存: map, slice, channel new 被用来分配除了引用类型的所有其他类型的内存: int, string, array等
这个例子会打印出什么?0还是10?。以上全错,运行的时候会painc,原因如下:
从这个提示中可以看出,对于引用类型的变量,我们不光要声明它,还要为它分配内容空间,否则我们的值放在哪里去呢?这就是上面错误提示的原因。 二者异同 所以从这里可以看的很明白了,二者都是内存的分配(堆上),但是make只用于slice、map以及channel的初始化(非零值);而new用于类型的内存分配,并且内存置为零。所以在我们编写程序的时候,就可以根据自己的需要很好的选择了。 make返回的还是这三个引用类型本身;而new返回的是指向类型的指针。 4.map map是引用类型的变量,底层是散列表,使用的时候必须进行初始化,在初始化的时候最好估算出map的容量,避免在运行的过程中动态的扩容。 5.将结构体序列化为JSON的时候字段必须为大写字母开头: 因为序列化的时候使用的bao为encoding/json由于不同包下不能访问小写字段所以不能解析到小写字母开头的字段。 6.work pool(goroutine 池) (1)限制goroutine开启的数量,防止goroutine暴增 (2)开启指定数量的协程,这三个协程不停地从通道中拿数据进行处理, 7.程序如何优雅的等待所有goroutine退出 (1)使用waitgroup实现优雅的等待所有goroutine退出,不推荐使用time.sleep() 8.使用goroutine并且要求该操作只执行一次需要使用sync.Once锁 只访问一次锁,Once底层是一个结构体,其中包括两个变量,sync.Mutex(互斥锁)类型变量,和一个标志位, once sync.Once 9.基本类型中的MAP类型不是并发安全的 如果需要多个goroutine同时访问修改Map类型变量的话需要使用sync包提供的map方法,sync.map,此方法声明的map变量不需要使用map初始化,开箱即用,此类型是并发安全的。 10.原子操作 原子操作就是对变量的并发操作是安全的,使用原子操作等同于使用加锁操作 使用atomic.AddInt64() 可以对int64类型的变量进行原子操作,此时是并发安全的 11.网络编程 (1)为什么会产生TCP黏包问题 (2)如何处理TCP黏包问题 大端,小端: 通过自定义协议处理:使用自定义的协议进行消息的编码和解码,解决TCP消息的黏包问题,该协议可以通过百度搜索获取,直接引入包即可使用 12.单元测试 (1)为什么需要编写单元测试函数? 不使用单元测试函数的话,每次需要测试一个函数都需要重写一次测试流程,比如重写打印输出语句,并且需要重新考虑所有的可能情况,因为交付代码的时候是不允许有大量的调试语句出现,这时候就可以通道单元测试进行测试,测试之后可以将测试的代码保留,方便下次测试。 (2)大型项目要求必须有单元测试 (3)单元测试文件必须以_test结尾,文件中的测试函数必须以Test开头,参数必须为 t *Testing 13.性能基础测试 基准测试就是在一定的工作负载之下检测程序性能的一种方法,基准测试的基本格式如下: func BenchmarkName(b*testing,B){....} 基准测试以Benchmark为前缀,需要一个*testing.B 类型的参数,基准测试必须要执行 b.N次,这样的测试才有对照性 (1)性能测试可以测试执行N次之后的平均执行时间 14.GO性能优化 (1)CPU性能优化,CPU.profile。 (2)Memory Profile (Heap Profile) ;报告程序内存使用情况。 (3)Block Profiling 报告goroutine不在运行状态的情况,可以用来分析和查找死锁等性能瓶颈。 (4)Groutine Profiling:报告goroutine的使用情况,有哪些goroutine,他们的调用关系是怎样的。
性能测试涉及的包设计两个包: runtime/pprof: 采集工具型,应用运行数据进行分析。 net/http/pprof:采集服务型,应用运行时数据进行分析。
pprof开启之后,每隔一段时间(10ms)就会收集下当前的堆栈信息,获取各个函数占用的CPU以及内存资源;最后通过对这些采样数据进行分析,形成一个性能分析报告。(注意这个比较占用系统资源,只有在性能分析的时候开启测试) 注意:我们只应该在性能测试的使用才在代码中引入pprof 15.使用go module进行包依赖管理 (1)vender包管理相对module来时比较老旧,功能上大同小异 (2)使用go module的话就不强制要求将代码写在gopath下
***需要确定使用gomodule如何管理下载的包,是存贮在某个文件夹中,还是记录在文件中每次编译都要重新下载 使用go module的优缺点 1、go module的优点 a、不必将项目放到GOPATH中 b、go build或go run 的时候,会自动拉取本地没有,但import的包。
16.Context(重点) (1)为什么要使用context: 控制并发有两种经典的方式,一种是WaitGroup,另外一种就是Context 还有一种方式:通过chan来实现进程间通信 (2)一个网络请求request,每个request都需要开启一个goroutine做一些事情。所以我们需要一种可以跟踪goroutine的方案才可以达到控制的目的,go为我们提供了Context (3)使用Context控制goroutine实现统一控制,为控制goroutine提供统一的标准。 Context的继承衍生
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc) func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) func WithValue(parent Context, key, val interface{}) Context 这四个With函数,接收的都有一个partent参数,就是父Context,我们要基于这个父Context创建出子Context的意思
Context使用原则 1.不要把Context放在结构体中,要以参数的方式进行传递 2.以Context作为参数的函数方法,应该把Context作为第一个参数,放在第一位 3.给一个函数方法传递Context的时候,不要传递nil,如果不知道传递什么,就使用context.TODO 4.Context的Value相关方法应该传递必须的数据,不要什么数据都使用这个传递 context管理goroutine 1.context上下文管理goroutine通过一个主goroutine管理子goroutine 2.这里的context类似于一棵树 3.这里的主goroutine就相当于根节点,通过根节点来创建子goroutine 4.这里的主goroutine就是main的执行单元(go的执行单元就是goroutine) 5.context.Background() 17。kafka(消息中间件) 支持分布式 18.etcd 19.函数式编程 (1)函数作为参数 使用函数类型的参数,首先定义一个该函数的类型,type functype functype,然后使用该类型定义函数类型参数 (2)函数作为返回值 20.使用go get golang.org/x/包被墙解决 使用go get github.com下的gopm工具来下载对应包的镜像可以解决被墙的问题 22.package初始化详细介绍 init()和main()函数: 1.这两个函数都是GO语言中的保留函数,init用于初始化信息,main()作为程序的入口 2.这两个行数定义的时候,不能有参数、返回值,只能由Go程序自动调用,不能被引用 3.init()函数可以定义在任意包中,可以有多个,main()函数只能在main包下,并且只能有一个 4.执行顺序: A: 先执行init()函数,在执行main()函数 B: 对于同一个GO文件中,init()执行书序从上到下,也就是先写的init()先被执行 C:对于同一个包下,将文件名按照字符串进行排序,之后顺序调用各个文件中的init()函数 D:对于不同包下: 如果不存在依赖,按照main包中import的顺序来调用对应包中的init()函数 如果存在依赖,最后被依赖的包的init()函数最先被调用: 导入顺序:main -> A ->B ->C 执行顺序:C ->B ->A ->main 5.存在依赖的包之间不能循环导入 6.一个包可以被多个包import但是只能被初始化一次 23.goroutine控制方式(CSP方式) (1)通过time.sleep() 此方法不优雅不推荐使用 (2)通过信号机制 使用os.single结合channel进行goroutine之间的通讯 func Notify func Notify(c chan<- os.Signal, sig ...os.Signal) Notify函数让signal包将输入信号转发到c。如果没有列出要传递的信号,会将所有输入信号传递到c;否则只传递列出的输入信号。 func Stop Stop函数让signal包停止向c转发信号。它会取消之前使用c调用的所有Notify的效果。当Stop返回后,会保证c不再接收到任何信号。 (3)通过等待组 sync.waitgroup控制goroutine (4)通过context上下文控制进程间通讯 ctx, cancle := context.WithCancel(context.Background()) go Watch(ctx, "[监控1]")
func Watch(ctx context.Context, name string){ for { select { case <- ctx.Done(): fmt.Println(name, "监控退出,停止了!") return default: fmt.Println(name,"goroutine进程监控中!") time.Sleep(time.Second*2) } } } (5)通过runtime.Gosched让调用的goroutine暂时让出系统资源,以后和其他协程公平竞争 (6) 通过runtimr.exit使调用的协程退出 |
请发表评论