《Go语言实战》读书笔记
- Go语言的介绍
1.1.2 并发
在执行一段代码的同时,并行去做另外一些事情,goroutine 是很好的选择。
package main
import "fmt"
func log(msg string) {
fmt.Printf("%s\n", msg)
}
func main() {
//log("检测到了错误")
go log("检测到了错误")
}
屏幕上什么都没有,程序没有正常工作 。Go程序从初始化main package并执行main()函数开始,当 main()函数返回时,程序退出,且程序并不等待其他goroutine(非主goroutine)结束。要让主函数等待所有goroutine退出后再返回,引出了多个goroutine之间并发通信。
知识点:
Go语言很特别:1.没有“对象”,没有继承多态,没有泛型,没有try/catch。2.有接口,函数式编程,CSP并发模型(goroutine+channel)。
基本语法要点:for, if后面的条件没有括号;if条件里也可以定义变量;没有while;switch不需要break,也可以直接switch多个条件。
第2章 快速开始一个Go程序
声明类型、变量、函数和方法:
变量定义:使用var关键字定义变量可放在函数内,或直接放在包内。使用:=定义变量,只能在函数内使用。
变量定义要点:常量定义包内,所有函数都能用;变量类型写在变量名之后;编译器可推测变量类型;没有char,只有rune;原生支持复数类型。
启动并同步操作 goroutine:
package main
import (
"fmt"
"time"
)
//定义一个函数,以goroutine的方式运行。使用c这个chan来通知
func worker(c chan int) {
for {
n := <-c
fmt.Println(n)
}
}
func chanDemo() {
c := make(chan int)
go worker(c)
c <- 1
c <- 2
time.Sleep(time.Millisecond)
}
func main() {
chanDemo()
}
控制台输出:
1
2
使用接口写通用的代码:
package main
import (
"os"
"fmt"
)
type Writer interface {
Write(b []byte) (n int, err error)
}
func main() {
var w Writer
// os.Stdout 实现 Writer接口
w = os.Stdout
fmt.Fprintf(w, "Hello, World!\n")
}
控制台输出:
Hello, World!
第 3 章 打包和工具链
使用 Go 的工具
wutao@wutao-PC:~/Downloads$ go build func.go
wutao@wutao-PC:~/Downloads$ go run func.go
Hello World!
使用 io 包的工作
package main
import (
"io/ioutil"
"fmt"
)
func main() {
const filename = "abc.txt"
if contents, err := ioutil.ReadFile(filename); err != nil {
fmt.Println(err)
} else {
fmt.Printf("%s\n", contents)
}
}
环境变量 GOPATH 决定了 Go 源代码在磁盘上被保存、编译和安装的位置。
可以为每个工程设置不同的 GOPATH ,以保持源代码和依赖的隔离。目前未配置GOPATH is empty。
第4章 数组、切片和映射
复制数组指针,只会复制指针的值,而不会复制指针所指向的值。
指针不能运算;参数传递:Go语言只有值传递一种方式。
package main
import (
"fmt"
)
func main() {
var a int = 2
var pa *int = &a
*pa = 3
fmt.Println(a)
}
控制台打印结果:3
函数语法:返回值类型写在最后面;可返回多个值;函数作为参数;没有默认参数,可选参数。
数组是值类型;切片本身没有数据,是对底层array的一中view视图。
定义一个空值切片,使用append方法为切片追加元素。
package main
import "fmt"
func main() {
var s []int
for i := 0; i < 100; i++ {
s = append(s, i)
}
fmt.Println(s)
}
第5章 Go 语言的类型系统
结构类型,定义一个二叉树
type TreeNode struct {
Left, Right *TreeNode
Value int
}
接口是声明了一组行为并支持多态的类型。嵌入类型提供了扩展类型的能力,而无需使用继承。Go语言仅支持封装,不支持继承和多态。没有class,只有struct。
第6章 并发
goroutine:
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 5; i++ {
go func(i int) {
for {
fmt.Printf("goroutine %d\n", i)
}
}(i)
}
time.Sleep(time.Millisecond)
}
每次执行main函数,控制台输出内容都不一样。
通道:
package main
import "fmt"
func chanTest() {
//使用 make 创建无缓冲的整型通道
c := make(chan int)
c <-1
n := <-c
fmt.Println(n)
}
func main() {
chanTest()
}
控制台日志
fatal error: all goroutines are asleep - deadlock!
package main
import "fmt"
func chanTest() {
//使用 make 创建无缓冲的整型通道
c := make(chan int)
//c <-1放在此处,会报错。
go func() {
for {
n := <-c
fmt.Println(n)
}
}()
c <-1
}
func main() {
chanTest()
}
控制台输出:1
无缓冲的通道(unbuffered channel)是指在接收前没有能力保存任何值的通道。这种类型的通道要求发送 goroutine 和接收 goroutine 同时准备好,才能完成发送和接收操作。如果两个 goroutine没有同时准备好,通道会导致先执行发送或接收操作的 goroutine 阻塞等待。
第7章 并发模式
runner、pool、work这3 个包分别实现了不同的并发模式。
runner模式可以理解为执行者,也就是来控制程序的执行,这种模式简单来说是控制 、执行,中断、退出。
runner 代码逻辑:创建 runner 结构体、创建简单工厂函数、创建任务添加方法、创建两个错误类型变量、创建run方法、创建start方法,监控任务,也就是run方法的包装。
在main函数中初始化并调用我们的runner,使用runner 管理工作。
pool包使用有缓冲的通道实现资源池。pool代码逻辑:创建资源池结构体、定义一个资源池已经关闭的错误、创建工厂函数、资源池关闭函数、资源释放函数。
work 模式的目的是展示如何使用无缓冲的通道来创建一个 goroutine 池。代码逻辑:定义Worker接口、创建资源池结构体、创建一个新的工作池、Run函数提交工作到工作池、资源池关闭函数。
第8章 标准库
log包:
package main
import (
"log"
)
func main() {
log.Println("I have something standard to say")
}
控制台输出:
2019/08/02 08:57:23 I have something standard to say
编码 JSON:
package main
import (
"encoding/json"
"fmt"
)
func main() {
type Cluster struct{
Cluster_name string
Cluster_uuid string
}
cluster := Cluster{
Cluster_name:"docker-cluster",
Cluster_uuid:"i_m4RzWfTDOe8t5d1HbHeg",
}
// 将这个映射序列化到 JSON 字符串
data, err := json.Marshal(cluster)
if err != nil {
fmt.Println("ERROR:", err)
return
}
fmt.Println(string(data))
}
控制台输出:
{"Cluster_name":"docker-cluster","Cluster_uuid":"i_m4RzWfTDOe8t5d1HbHeg"}
解码 JSON:
package main
import (
"encoding/json"
"fmt"
)
// JSON 包含要反序列化的样例字符串
var JSON = `{
"Cluster_name":"docker-cluster",
"Cluster_uuid":"i_m4RzWfTDOe8t5d1HbHeg"
}`
func main() {
type Cluster struct{
Cluster_name string
Cluster_uuid string
}
cluster := Cluster{}
// 将JSON 字符串反序列化到变量
err := json.Unmarshal([]byte(JSON), &cluster)
if err != nil {
fmt.Println("ERROR:", err)
return
}
fmt.Println(cluster)
}
控制台打印:
{docker-cluster i_m4RzWfTDOe8t5d1HbHeg}
第9章 测试和性能
单元测试
main.go:
package main
import "fmt"
func Subtract(a, b int) int {
return a-b
}
func main() {
fmt.Println(Subtract(5,2))
}
main_test.go:
package main
import "testing"
func TestSubtract(t *testing.T) {
result := Subtract(5, 2)
if result != 3 {
t.Error("5 minus 2 result isn't 3")
}
}
wutao@wutao-PC:~/Desktop$ go test main_test.go main.go -v -cover
=== RUN TestSubtract
--- PASS: TestSubtract (0.00s)
PASS
coverage: 50.0% of statements
ok command-line-arguments 0.002s coverage: 50.0% of statements
wutao@wutao-PC:~/Desktop$ go test main_test.go main.go
ok command-line-arguments 0.001s
表组测试:
package main
import "testing"
func TestSubtract1(t *testing.T) {
var data = []struct {
args1 int
args2 int
suntract int
}{
{5,2, 3,},
{-2, -1, -1},
}
for _, test := range data {
subtract := Subtract(test.args1, test.args2)
if subtract != test.suntract {
t.Errorf("%v minus %v result isn't %v", test.args1, test.args2, test.suntract)
}
}
}
wutao@wutao-PC:~/Desktop$ go test main_test.go main.go -v -cover
=== RUN TestSubtract1
--- PASS: TestSubtract1 (0.00s)
PASS
coverage: 50.0% of statements
ok command-line-arguments 0.001s coverage: 50.0% of statements