《Go语言实战》中文版练习案例

《Go语言实战》读书笔记

  1. 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值