Go语言基础
规则
大写字母开头变量为共有变量,小写字母开头为私有变量(函数类似)
Iota
iota枚举(默认开始值为0,const中每增加一行,值加1)
slice
引用类型(类似于动态数组),相比于array,它没有长度限制
make & new
- make返回初始化后的值(非零),只能被用于slice、map、channel
- new返回指针,一般很少被使用
goto(无条件跳转,配合标签使用,无条件跳转至标签,可用于跳出循环)
由于可随意跳转,在庞大的系统中不利于debug,所以在coding中会通过结构化程序设计来规避这类问题,那么,在go语言设计者为何会继续沿用goto呢?
- 最开始提出goto有害论主要是当时的背景导致的,最开始使用的是非结构化的过程语言,例如BASIC和各种汇编语言
- 可读性强,可以书写出干净的退出功能(将所有退出方式统一编辑,利用goto进行跳转)
- 低级性能改进,goto语句执行的非常快
defer(延迟语句,函数执行到最后会按照逆序执行defer语句,可用于处理资源泄漏问题)
- 在函数中使用时,采用先进后出模式
panic & recover(go没有异常机制,使用这两个内置函数来代替异常机制)
- 调用panic中断函数执行(但defer语句仍然会被执行),退出执行
- 利用recover + defer可以捕获panic的输入值,并恢复正常执行
package main
import "fmt"
func test_goto() {
defer func() {
if err := recover(); err != nil {
fmt.Println(err)
}
}()
for sum := 0; sum < 15; sum++ {
if sum == 12 {
panic("12 is error")
}
fmt.Println(sum)
}
}
func main() {
test_goto()
fmt.Println("over!")
}
输出结果为:
0
1
2
3
4
5
6
7
8
9
10
11
12 is error
over!
使用指针作为receiver
如果不使用指针作为receiver,那么传递的是参数的copy,如果修改值,只会作用在copy上
interface
GoLang本身是不支持泛型(通用类型)编程的,这也是它被诟病最多的地方,为了弥补这一缺陷,可以通过interface来实现泛型编程(PS:go只是暂时不支持,因为这会增加设计的复杂度,以后可能会支持)
反射
Go利用reflect实现反射(即检查程序在运行时的状态),要去反射是一个类型的值 (这些值都实现了空 interface),首先需要把它转化成 reflect 对象 (reflect.Type 或者 reflect.Value,根据不同的情况调用不同的函数)。
要反射的字段必须是可读写的字段,否则会报错。
并发
goroutine
goroutine 是 Go 并行设计的核心。goroutine 说到底其实就是协程,但是它比线程更小,十几个 goroutine 可能体现在底层就是五六个线程,Go 语言内部帮你实现了这些 goroutine 之间的内存共享。执行 goroutine 只需极少的栈内存 (大概是 4~5 KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine 比 thread 更易用、更高效、更轻便。
设计上我们要遵循:不要通过共享来通信,而要通过通信来共享。
但在 Go 1.5 以前调度器仅使用单线程,也就是说只实现了并发。想要发挥多核处理器的并行,需要在我们的程序中显式调用 runtime.GOMAXPROCS (n) 告诉调度器同时使用多个线程。GOMAXPROCS 设置了同时运行逻辑代码的系统线程的最大数量,并返回之前的设置。如果 n < 1,不会改变当前设置。
channel
channel类型的值可以发送接收值,注意,必须使用 make 创建 channel。默认情况下,channel 接收和发送数据都是阻塞的,除非另一端已经准备好,这样就使得 Goroutines 同步变的更加的简单,而不需要显式的 lock。所谓阻塞,也就是如果读取(value := <-ch)它将会被阻塞,直到有数据接收。其次,任何发送(ch<-5)将会被阻塞,直到数据被读出。无缓冲 channel 是在多个 goroutine 之间同步很棒的工具。
Go 也允许指定 channel 的缓冲大小,很简单,就是 channel 可以存储多少元素。ch:= make (chan bool, 4),创建了可以存储 4 个元素的 bool 型 channel。在这个 channel 中,前 4 个元素可以无阻塞的写入。当写入第 5 个元素时,代码将会阻塞,直到其他 goroutine 从 channel 中读取一些元素,腾出空间。
select
select可以监听channel上的数据流动。select默认是阻塞的,只有当监听的 channel 中有发送或接收可以进行时才会运行,当多个 channel 都准备好的时候,select 是随机的选择一个执行的。
select {
case i := <-c:
// use i
default:
// 当 c 阻塞的时候执行这里
}
select其实就是类似 switch 的功能,default 就是当监听的 channel 都没有准备好的时候,默认执行的(select 不再阻塞等待 channel)。
我们可以使用select来设置超时以避免goroutine阻塞导致的整个程序阻塞。
func main() {
c := make(chan int)
o := make(chan bool)
go func() {
for {
select {
case v := <- c:
println(v)
case <- time.After(5 * time.Second):
println("timeout")
o <- true
break
}
}
}()
<- o
}
来源:CSDN
作者:Programmer_Evelyn
链接:https://blog.csdn.net/qq_29279253/article/details/104004640