[ GO语言工程实践 | 青训营笔记 ]

94 阅读4分钟

一、 并发与并行

并发是指一个处理器同时处理多个任务。
并行是指多个处理器或者是多核的处理器同时处理多个不同的任务。
并发是逻辑上的同时发生,而并行是物理上的同时发生。
来个比喻:并发是一个人同时吃三个馒头,而并行是三个人同时吃三个馒头

协程:用户态,轻量级线程,

线程:内核态,线程跑多个协程。

二、 Goroutine

Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。

在 Go 语言中,协程的实现是基于 Go 运行时系统的。在程序启动时,Go 运行时系统会创建一个主协程,该协程负责程序的初始化和启动。在程序运行过程中,通过 go 关键字可以创建新的协程.

实例:

go
复制代码
package main  
  
import (  
        "fmt"  
       )

func main() {

    go func() {

        // 协程代码

    }()

    // 主协程代码

}

三、通道

通道(channel)是用来传递数据的一个数据结构。

1.2 通道的声明

声明一个通道和定义一个变量的语法一样:

// 声明通道

go
复制代码
var 通道名 chan 数据类型

// 创建通道:如果通道为nil( 就是不存在 ) ,就需要先创建通道

go
复制代码
通道名 = make(chan 数据类型)

示例代码:

go
复制代码
package main

 import "fmt"
 
 func main() {
 
   var a chan int

    if a == nil {

       fmt.Println("channel 是 nil 的, 不能使用,需要先创建通道。。")

       a = make(chan int)

       fmt.Printf("数据类型是: %T", a)

    }

}

运行结果:

go
复制代码
channel 是 nil 的, 不能使用,需要先创建通道。。

数据类型是: chan int

 

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

arduino
复制代码
ch <- v    // 把 v 发送到通道 ch
go
复制代码
v := <-ch  // 从 ch 接收数据
arduino
复制代码
           // 并把值赋给 v

声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:

go
复制代码
ch := make(chan int)

通道缓冲区

通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:

go
复制代码
ch := make(chan int, 100)

除了使用 go 关键字创建协程外,Go 语言还提供了一些其他的协程相关的函数,例如:

· runtime.Gosched():主动让出 CPU 时间片,让其他协程有机会运行。

· sync.WaitGroup:等待一组协程执行完毕后再继续执行。

四、依赖管理

1.  path

gopath 可以理解为go语言的工作空间,内部存储了src,bin,pkg 三个文件夹

image.png

  • gopath具有多个作用,当我们想从github或其他地方获取go项目代码时,我们可以使用go get指令。 此时程序会默认的将代码存储到$GOPATH/src目录中。例如拉取go get github.com/dreamerjackson/theWayToGolang时,目录结构如下:

image.png

gopath 的优劣

  • 相比于其他语言繁琐的配置,go语言中的工作空间gopath配置相对简单,容易理解
  • gopath使得在文件系统组织整个代码更加简洁、结构化,但是限制在单一的工作空间中。
  • gopath并没有解决版本依赖的问题,而将其留给了其他工具去实现。正因为如此,gopath中的代码就是一个唯一的master分支,并且强制使用各个模块最新的代码。

2.   go vendor

govendor

govendor 是一个基于 vendor 机制实现的 Go 包依赖管理命令行工具。与原生 vendor 无侵入性融合,也支持从其他依赖管理工具迁移,可以很方便的实现同一个包在不同项目中不同版本、以及无相互侵入的开发和管理。

在执行 go build 或 go run 命令时,会按照以下顺序去查找包:

  • 当前包下的 vendor 目录
  • 向上级目录查找,直到找到 src 下的 vendor 目录
  • 在 GOROOT 目录下查找
  • 在 GOPATH 下面查找依赖包

 

解决的问题

将源码拷贝到当前目录下,这样导包当前工程代码到任意的机器的 ¥GOPATH/src 都可以编译通过,避免项目代码外部依赖过多

未解决的问题

无法精确的引用 外部包进行版本控制,不能指定引用某个特定版本的外部包,只是在开发时将其拷贝过来,但是一旦外部包升级,vendor 下面的包会跟着升级,而且 vendor 下面没有完整的引用包的版本信息, 对包升级带来了无法评估的风险。

3.   go-mod

go module是Go1.11版本之后官方推出的版本管理工具,并且从Go1.13版本开始,go module将是Go语言默认的依赖管理工具。

包不再保存在GOPATH中,而是被下载到了$GOPATH/pkg/mod路径下.

go mod vendor 会将依赖包放到 vendor 目录

五、 单元测试(unit test)

测试 package main 下 calc.go 中的函数,要只需要新建 calc_test.go 文件,在calc_test.go中新建测试用例即可。

// calc.go

go
复制代码
package main 

func add(num1 int, num2 int) int { 

return num1 + num2 
}

// calc_test.go

go
复制代码
package main  
import "testing" 
func TestAdd(t *testing.T) { 	
if ans := add(1, 2); ans != 3 { 	
t.Error("add(1, 2) should be equal to 3") 
}
} 

运行 go test,将自动运行当前 package 下的所有测试用例,如果需要查看详细的信息,可以添加-v参数。

diff
复制代码
$ go test -v 
=== RUN TestAdd 
--- PASS: TestAdd (0.00s)
PASS 
ok      example 0.040s

QA ;简单应该是 Golang 最大的优势。Golang 的语言特性简单,学习周期短,熟悉其他编程语言的开发者基本都可以在短时间学会并写出各方面都还不错的代码。所谓各方面都还不错是说新手开发者写出来的代码和一些有经验的开发者写出来的代码差别并不会太大。golang是一门很有前途的语言。