这是我参与「第五届青训营 」伴学笔记创作活动的第 2 天
一、本堂课内容:
1. 并发编程
协程 Goroutine
协程是Go语言中的一种轻量级线程,它可以在一个线程中并发执行多个函数。使用协程可以大大提高程序的并发能力。
使用go关键字可以开启一个协程,如下所示:
package main
import "fmt"
func main() {
go func() {
fmt.Println("Hello, I'm a goroutine!")
}()
fmt.Println("Hello, I'm the main function!")
}
在上面的代码中,我们使用了go关键字来开启一个协程,该协程会在背后并发执行。我们还可以使用channel来进行协程间的通信,如下所示:
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
fmt.Println(<-ch)
}
在上面的代码中,我们使用了channel来进行协程间的通信。
通道 Channel
通道是Go语言中用于进行协程间通信的重要工具。通道可以用于协程间的数据传递,也可以用于同步协程的执行。
通道可以使用make函数创建,还可以指定channel 的方向,如下所示:
Copy code
package main
import "fmt"
func main() {
ch := make(chan int)
go process(ch)
for {
select {
case <-ch:
fmt.Println("receive from channel")
return
default:
fmt.Println("sleep 1 second")
}
time.Sleep(time.Second)
}
}
func process(ch chan<- int) {
defer func() {
ch <- 1
}()
// do something
}
在上面的代码中,我们使用了make函数创建了一个int类型的通道,并使用<-运算符将数据写入通道和读取通道中的数据。
线程同步 WaitGroup
WaitGroup是Go语言中用于线程同步的重要工具。它可以用来等待多个线程完成后再继续执行。
使用方法如下:
package main
import (
"fmt"
"sync"
)
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
fmt.Println("Hello, I'm a goroutine!")
wg.Done()
}()
}
wg.Wait()
fmt.Println("All goroutines finished!")
}
在上面的代码中,我们使用了sync.WaitGroup来等待所有的协程完成后再继续执行。在每个协程执行前,我们都会使用wg.Add(1)来增加计数器,在每个协程执行结束后,我们都会使用wg.Done()来减少计数器。最后我们使用wg.Wait()来等待所有的协程完成,当计数器为0时,wg.Wait()函数才会返回。
锁 Mutex
锁是Go语言中用于保证线程安全的重要工具。锁可以用来保证在多个线程中对共享资源的互斥访问。
Go语言标准库中提供了sync包来支持锁的使用。其中最常用的锁是sync.Mutex。使用方法如下:
Copy code
package main
import (
"fmt"
"sync"
)
var counter int
var lock sync.Mutex
func main() {
for i := 0; i < 10; i++ {
go func() {
lock.Lock()
defer lock.Unlock()
counter++
fmt.Println(counter)
}()
}
}
在上面的代码中,我们使用了sync.Mutex来保证counter的并发安全。在每次访问counter之前,我们都会使用lock.Lock()来上锁,在访问结束后使用lock.Unlock()来解锁。
2. 依赖管理
直接使用 go module,gopath 默认的就行,如果有命令行工具的话,再把 gopath/bin 加到 PATH 里面
- Gopath
Gopath是Go语言中最早的依赖管理方式。它是使用的依赖管理方式是Gopath。Gopath是Go语言的工作空间目录,其中包含了第三方包和项目的源码。开发者可以手动将第三方包下载到Gopath目录中,然后在项目中使用import导入。
在Gopath中,代码的目录结构如下图所示:
- $GOPATH
- src
- github.com
- user
- project
其中,$GOPATH是环境变量,src目录下是所有的源码目录,github.com目录下是所有的第三方包目录。
- Go Vendor
Go Vendor是Go语言中比较新的依赖管理方式。它是通过在项目目录中创建vendor目录来管理依赖包。
在Go Vendor中,代码的目录结构如下图所示:
- project
- vendor
- github.com
- user
- project
其中,project目录是项目的根目录,vendor目录下是所有的依赖包目录。
- Go Module
Go Module 是Go语言中最新的依赖管理方式。Go Module 是一种基于版本的依赖管理工具,可以自动管理依赖的版本兼容性。开发者可以在项目中使用 go mod init 命令来初始化Go Module,然后使用 go get/mode 命令来管理依赖。
在Go Module中,代码的目录结构如下图所示:
- project
- go.mod
- go.sum
其中,project目录是项目的根目录,go.mod文件记录了项目的依赖信息,go.sum文件记录了项目依赖的校验和。
3. 单元测试
单元测试概念和规则
单元测试是指对于程序中的独立部分进行测试。在Go语言中,单元测试是通过testing包来实现的。
单元测试的规则如下:
- 每个单元测试文件都必须以_test.go为后缀。
- 每个单元测试函数都必须以Test开头。
- 单元测试函数的第一个参数必须是testing.T类型。
示例代码如下:
package main
import "testing"
func TestAdd(t *testing.T) {
result := add(1, 2)
if result != 3 {
t.Error("Expected 3, got", result)
}
}
func add(a, b int) int {
return a + b
}
基准测试
基准测试是指对于程序中的性能进行测试。在Go语言中,基准测试是通过testing包来实现的。
基准测试的规则如下:
- 每个基准测试函数都必须以Benchmark开头。
- 基准测试函数的第一个参数必须是*testing.B类型。
示例代码如下:
package main
import "testing"
func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
add(1, 2)
}
}
func add(a, b int) int {
return a + b
}
二、课后个人总结:
- Go语言中的并发编程的几个重要工具:协程、通道、锁和线程同步。这些工具可以帮助我们在多核处理器上提高程序的并发能力,并保证线程安全。
- 从Go语言1.0版本发布到现在,Go团队不断提供了新的工具来管理依赖。从最初的Gopath到后来的Go Vendor和Go Module,Go团队提供了更加方便、稳定和高效的依赖管理工具。现在,Go Module已经成为Go语言的官方依赖管理工具。
- 单元测试是对于程序中独立部分进行测试,可以保证程序的正确性。而基准测试是对于程序性能进行测试,可以保证程序的高效性。使用单元测试和基准测试可以保证程序的正确性和高效性。