Go基础语法和常用特性 | 青训营

118 阅读7分钟

基础语法

输出语句

主要通过标准库中的fmt包调用输入输出语句。在输出上主要有三种输出函数

  1. fmt.Print:直接输出内容,采用默认格式将其参数格式化并写入标准输出。如果两个相邻的参数都不是字符串,会在它们的输出之间添加空格。返回写入的字节数和遇到的任何错误。
  2. fmt.Printf:支持格式化输出字符串,根据format参数生成格式化的字符串并写入标准输出os.stdout。返回写入的字节数和遇到的任何错误。
  3. fmt.Println:能够在输出内容的结尾添加换行符,采用默认格式将其参数格式化并写入标准输出。总是会在相邻参数的输出之间添加空格并在输出结束后添加换行符。返回写入的字节数和遇到的任何错误。

例如:

func main() { 
    fmt.Println("XIXIXI") 
    fmt.Print("LALALA") 
    name := "123" 
    fmt.Printf("我是:%s\n", name) 
    fmt.Println("XIXIXI") 
    }

输出为:

XIXIXI
LALALA我是:123
XIXIXI

变量声明

存在两种声明方式。一种是指定变量类型和初始值;另一种是通过:=,由系统自动推导变量的类型。例如:

//第一种声明方式
var a int = 1
var b string
b = "lalala"

//第二种声明方式
c := 4
d := b + "xixixi"

相比Python,Go更强调语法的规范性;但比起C++和Java,也能使用:=来确保简洁。

循环

Go中的 for 循环可以实现其它语言中的 for、while循环和遍历迭代。直接使用for循环可以达到类似while ture的结果,即无限循环,只能使用break跳出。配合判断语句达成while循环的效果。

//第一种形式,类似C的for
//for init; condition; post { }
for i := 1; i < 10; i++ {
    fml.println(i)
}

//第二种形式,类似while
//for condition { }
j := 1
for j < 3 {
    println(j)
    j += 1
}

//第三种形式,无限循环
for {
    println("lalala")
    break
}

//第四种,利用range进行循环
c := "abc"
for k := range c {
    println(c[k]) //输出a、b、c
}

数组

数组是内置类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。在初始化后长度是固定的,无法修改其长度。当作为方法的参数传入时将复制一份数组而不是引用同一指针。数组的长度也是其类型的一部分,通过内置函数len(array)获取其长度。比较特殊的,在创建动态数组时,需要利用make函数。

例如:

s := []int {1,2,3} //直接初始化切片  
  
s := arr[:] //用数组初始化切片  
  
s = make([]int, 3) //make初始化,有3个元素的切片, len和cap都为3  
  
s = make([]int, 2, 3) //make初始化,有2个元素的切片, len为2, cap为3

s = make([]int, a) //make初始化,a为变量

指针

Go提供了控制数据结构指针的能力,但是不能进行指针运算。指针(pointer)在Go语言中可以被拆分为两个核心概念:

  • 类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算。
  • 切片,由指向起始元素的原始指针、元素数量和容量组成。

受益于这样的约束和拆分,Go语言的指针类型变量即拥有指针高效访问的特点,又不会发生指针偏移,从而避免了非法修改关键性数据的问题。同时,垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。

常用特性

并发

Go语言引入了goroutine概念,它使得并发编程变得非常简单。使用 go 关键字就可以创建 goroutine,将 go 声明放到一个需调用的函数之前,在相同地址空间调用运行这个函数,这样该函数执行时便会作为一个独立的并发线程,这种线程在Go语言中则被称为 goroutine。

参考blog.csdn.net/qq_41854911… 例如:

//go 关键字放在方法调用前新建一个 goroutine 并执行方法体 
go GetThingDone(param1, param2); 
//新建一个匿名方法并执行 
go func(param1, param2) { 
}(val1, val2) //直接新建一个 goroutine 并在 goroutine 中执行代码块 
go { 
//do someting... 
}

为了解决并发编程时存在的共享资源竞争问题,即多个goroutine相互覆盖结果,可以通过go build -race命令进行检查。在项目目录下执行这个命令,生成一个可以执行文件,然后再运行这个可执行文件,就可以看到打印出的检测信息。

由于一个进程内创建的所有goroutine运行在同一个内存地址空间中,因此如果不同的goroutine不得不去访问共享的内存变量,访问前应该先获取相应的读写锁。Go语言标准库中的sync包提供了完备的读写锁功能。

垃圾回收

Go采用标记清除算法实现垃圾回收,并在此基础上使用三色标记法和写屏障技术提高效率。 一次完整的 GC 分为四个阶段:

  • 1)标记准备(Mark Setup,需 STW),打开写屏障(Write Barrier)
  • 2)使用三色标记法标记(Marking, 并发)
  • 3)标记结束(Mark Termination,需 STW),关闭写屏障。
  • 4)清理(Sweeping, 并发)

标记准备阶段最重要的任务是清扫上一阶段GC遗留需要清扫的垃圾。同时,标记准备阶段还会重置各种状态和指标,启动专门标记的协程,统计需要扫描的任务数,开启写屏障,启动标记协程等等。

三色并发标记法把把遍历对象图过程中遇到的对象,按 “是否访问过” 这个条件标记成以下三种颜色:

  • 白色:尚未被GC访问过的对象,如果全部标记已完成依旧为白色的,称为不可达对象,既垃圾对象。
  • 黑色:本对象已经被GC访问过,且本对象的子引用对象也已经被访问过了。
  • 灰色:本对象已访问过,但是本对象的子引用对象还没有被访问过,全部访问完会变成黑色,属于中间态。

在GC并发标记刚开始时,所有对象均为白色集合;将所有GCRoots直接引用的对象标记为灰色集合;判断灰色集合中的对象:若对象不存在子引用,则将其放入黑色集合;若对象存在子引用对象,则 将其所有的子引用对象放入灰色集合,当前对象放入黑色集合。以此类推,直至灰色集合中的所有对象变成黑色后,本轮标记完成,且当前白色集合内的对象称为不可达对象,即垃圾对象。

组合插入写屏障和删除写屏障构成了混合写屏障,保证弱三色不变性;该写屏障会将覆盖的对象标记成灰色(删除写屏障)并在当前栈没有扫描时将新对象也标记成灰色(插入写屏障):写屏障会将被覆盖的指针和新指针都标记成灰色,而所有新建的对象都会被直接标记成黑色。混合写屏障的具体核心规则如下:

  1. GC开始将对象全部扫描并标记为黑色(之后不再进行第二次重复扫描,无需STW),
  2. GC期间,任何创建的新对象,均为黑色。
  3. 被删除的对象标记为灰色。
  4. 被添加的对象标记为灰色。