GoLang Channel and

通道作为 Go 的特性之一,其主要作用是用来实现并发同步

通道与 Go 的另一个特性协程一起,构成了 Go 的并发编程。

什么是通道?

一般的理解来看,实现并发同步的两种思路分别是:

  • 通过内存共享通讯

  • 通过通讯共享内存

Go 中的通道即是采用第二种思路设计。

可以把通道看做是一个先进先出的队列,一些协程可以向通道发送数据,另一些协程则可以从通道中接收数据。

通道类型和值

GoLang Basic Knowledge

语言特色

  • 简洁、快速、安全
  • 并行、有趣、开源
  • 内存管理、数据安全、编译迅速

关键字和标识符

关键字

关键字是一些特殊的用来帮助编译器理解和解析源代码的单词。

1
2
3
4
5
break     default      func    interface  select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var

标识符

一个标识符是一个以Unicode字母或者_开头并且完全由Unicode字母和Unicode数字组成的单词。

1
2
3
player
_prev
next_1

基本类型、变量和常量

基本内置类型

Go 支持内置基本类型(预声明类型)有:

  • 布尔类型 bool

  • 整数类型 int8uint8int16uint16int32uint32int64uint64intuintuintptr

  • 浮点数类型 float32float64

  • 复数类型 complex32complex64

  • 字符串类型 string

通常,intuintuintptr 的尺寸依赖于具体的编译器实现。

变量

Go 中声明变量一般使用 var 关键字。

1
var identifier type 

声明变量时,如果指定了变量类型但没有初始化,则变量的默认值为指定类型的零值:

1
2
3
4
5
6
var numericalVal int
println(numericalVal) // 0
var stringVal string
println(stringVal) // ''
var booleanVal bool
println(booleanVal) // false

如果没有指定变量类型,但进行了初始化,Go 会自动推断变量的类型:

1
2
var valF, valS, valT = "", 100, 100.10
fmt.Printf("valF = %T, valS = %T, valT = %T \n", valF, valS, valT) // valF = string, valS = int, valT = float64

还可以使用 := 来声明变量,只能用在函数体重:

1
2
valF := "Anthor var"
fmt.Printf("valF value = %v and type = %T", valF, valF) // valF value = Anthor var and type = string

声明全局变量:

1
2
3
4
5
var (
valI int
valB bool
valS string
)

如果不需要某个值,可以使用 _ 将其抛弃:

1
2
var valA, valB = 1, 2
_, valB = 3, 4

常量

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。

1
2
const valBoolean, valInt, valFloat, valString = false, 10, 3.14, "string"
fmt.Printf("%v %v %v %v", valBoolean, valInt, valFloat, valString) // false 10 3.14 string

特殊常量值 itoa

1
2
3
4
5
6
const (
a = iota
b
c
)
fmt.Printf("%v %v %v \n", a, b, c) // 0 1 2

函数

Go 中使用 func 关键字来声明一个函数

1
func funcName([params list]) [return types] {}

数组

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。

数组的一般声明方式:

1
var arrayName [arraySize] elementType

初始化数组:

1
2
3
4
5
6
7
8
var intArray = [3]int{1, 2, 3}
fmt.Printf("%T %v \n", intArray, intArray) // [3]int [1 2 3]

stringArray := [3]string{"a", "b", "c"}
fmt.Printf("%T %v \n", stringArray, stringArray) // [3]string [a b c]

booleanArray := [...]bool{true, false}
fmt.Printf("%T %v \n", booleanArray, booleanArray) // [2]boole [true false]

指针

一个指针变量指向了一个值的内存地址。

指针的使用类似于变量和常量,在使用指针之前需要声明它:

1
var pointerName *pointerType

举几个例子:

1
2
var intPointer *int
var stringPointer *bool

简单使用:

1
2
3
4
var intPointer *int
var intVal = 99
intPointer = &intVal
fmt.Printf("%v %v %v", intPointer, &intPointer, *intPointer) // 0xc0000b2008 0xc0000ac018 99

一个指针变量声明后如果没有被赋值,它的值为 nil ,称为空指针。

结构体

Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。

结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。

定义结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import "fmt"

type student struct {
name string
age int
height float32
weight float32
}

func main() {
var studentA = student{"Mike", 18, 178, 78.9 }
fmt.Printf("%v %T \n", studentA, studentA) // {Mike 18 178 78.9} main.student
fmt.Printf("name = %v \n", studentA.name) // Mike
fmt.Printf("age = %v \n", studentA.age) // 18

// 结构体指针
var studentAPointer = &studentA
fmt.Printf("%v %T \n", studentAPointer, studentAPointer) // &{Mike 18 178 78.9} *main.student
fmt.Printf("name = %v \n", studentAPointer.name) // Mike
fmt.Printf("age = %v \n", studentAPointer.age) // 18
}

切片

Go 语言切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

定义切片:

1
2
3
4
5
6
7
8
9
10
11
var numberSlice []int
numberSlice = make([]int, 3)
fmt.Printf(" %v %T \n", numberSlice, numberSlice) // [0, 0, 0] []int

stringSlice := []string{"a", "b", "c"}
fmt.Printf("%v %T \n", stringSlice, stringSlice) // [a, b, c] []string

// 从数组创建
floatArray := [...]float32{1.1, 2.2, 3.3}
floatSlice := floatArray[0:2]
fmt.Printf("%v %T \n", floatSlice, floatSlice) [1.1, 2.2] []float32

切片的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	intSlice := []int{1, 2, 3, 4, 5, 6}
fmt.Printf("slice length = %v \n", len(intSlice)) // slice length = 6
fmt.Printf("slice capacity = %v \n", cap(intSlice)) // slice capacity = 6

// slice 取值
fmt.Printf("slice [3] = %v \n", intSlice[3]) // slice[3] = 4
fmt.Printf("slice [0:1] = %v \n", intSlice[0:1]) // slice[0:1] = [1]
fmt.Printf("slice [0:2] = %v \n", intSlice[0:2]) // slice[0:2] = [1, 2]
fmt.Printf("slice [:3] = %v \n", intSlice[:3]) // slice[:3] = [1, 2, 3]
fmt.Printf("slice [3:] = %v \n", intSlice[3:]) // slice[3:] = [4, 5, 6]

// append() 和 copy()
intSlice = append(intSlice, 7)
fmt.Printf("slice = %v \n", intSlice) // slice = [1 2 3 4 5 6 7]
intSlice = append(intSlice, 8, 9, 10)
fmt.Printf("slice = %v \n", intSlice) // slice = [1 2 3 4 5 6 7 8 9 10]
anotherSlice := []int{11, 12, 13}
intSlice = append(intSlice, anotherSlice...)
fmt.Printf("slice = %v \n", intSlice) // slice = [1 2 3 4 5 6 7 8 9 10 11 12 13]
thirdSlice := []int{88, 99}
copy(intSlice, thirdSlice)
fmt.Printf("slice = %v \n", intSlice) // slice = [88 99 3 4 5 6 7 8 9 10 11 12 13]

范围

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
intSlice := []int{1, 3}

for index, num := range intSlice {
fmt.Printf("index = %v num = %v \n", index, num)
}
// index = 0 num = 1
// index = 1 num = 3


stringMap := map[string]string{"a": "A", "b": "B"}
for key, val := range stringMap {
fmt.Printf(" key = %v val = %v \n", key, val)
}
// key = a val = A
// key = b val = B

集合

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

定义 Map:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var numMap map[string]int
numMap = make(map[string]int)
numMap["a"] = 1
fmt.Printf("map = %v \n", numMap) // map = map[a:1]

stringMap := make(map[string]string)
stringMap["name"] = "Mike"
stringMap["age"] = "十八"

for key, val := range stringMap {
fmt.Printf("key = %v value = %v \n", key, val)
}
// key = name value = Mike
// key = age value = 十八

delete(stringMap, "age")
fmt.Printf("map = %v \n", stringMap) // map = map[name:Mike]