Go语言第2课工程实践| 青训营笔记

129 阅读3分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第1篇笔记

课程资料

课程代码

PPT链接

课堂回放

课程内容

1.Go语言进阶-从并发视角了解

并发:多线程在单核CPU运行

并行:多线程在多核CPU运行

image.png image.png

协程可以理解为轻量级的线程,go语言可以一次创建上万个协程,因此适合高并发场景

image.png

go语言开启协程

func hello(i int) {
	println("hello goroutine: " + fmt.Sprint(i))
}
func HelloGoRoutine() {
	for i := 0; i < 5; i++ {
		go func(j int) { //调用函数时加个go即可开启协程
			hello(j)
		}(i)
	}
	time.Sleep(time.Second)
}

通过并行输出

image.png

解决同步问题:使用有缓冲区通道

package main

func CalSquare() {
	src := make(chan int) //无缓冲队列
	dest := make(chan int, 3)
	go func() { //协程 A,产生数字
		defer close(src)
		for i := 0; i < 10; i++ {
			src <- i
		}
	}()
	go func() { //协程 B,遍历数字并计算平方,发送到dest(有缓冲)
		defer close(dest) 
		for i := range src {
			dest <- i * i
		}
	}()
	for i := range dest { //遍历dest
		//复杂操作
		println(i)
	}
}
func main() {
	CalSquare()
}

image.png

并发安全问题

package main

import (
	"sync"
	"time"
)

var (
	x    int64
	lock sync.Mutex
)

func addWithLock() {
	for i := 0; i < 2000; i++ {
		lock.Lock()// 通过 lock获取临界区资源,保证并发安全
		x += 1
		lock.Unlock()// 获取之后释放
	}
}
func addWithoutLock() {
	for i := 0; i < 2000; i++ {
		x += 1
	}
}
func Add() {//每个函数开启5个协程
	x = 0
	for i := 0; i < 5; i++ {
		go addWithoutLock()
	}
	time.Sleep(time.Second)
	println("WithoutLock:", x)

	x = 0
	for i := 0; i < 5; i++ {
		go addWithLock()
	}
	time.Sleep(time.Second)
	println("WithLock:", x)

}
func main() {
	Add()
}

image.png

WaitGroup 优化

package main

import (
	"fmt"
	"sync"
	"time"
)

func hello(i int) {
	println("hello goroutine: " + fmt.Sprint(i))
}
func HelloGoRoutine() {
	for i := 0; i < 5; i++ {
		go func(j int) {
			hello(j)
		}(i)
	}
	time.Sleep(time.Second)
}
func ManyGoWait() {
	var wg sync.WaitGroup
	wg.Add(5) //开启5个协程
	for i := 0; i < 5; i++ {
		go func(j int) {
			defer wg.Done() //计数器-1,表明子协程任务结束
			hello(j)
		}(i)
	}
	wg.Wait() //进行堵塞
}

func main() {

	//HelloGoRoutine()
	ManyGoWait()
}

2.Go语言依赖管理

利用已封装的开发组件/工具提高自己工作效率

image.png

gopath即工作区,工程文件在src下,go get下载最新包到源码目录下

通过gomod管理包

2.1配置gomod过程

由于使用vscode作编辑器,需要在setting作如下配置(不设置好像也行,我之后把设置文件删了仍可以运行)

"go.useLanguageServer": true,
    "[go]": {
        "editor.insertSpaces": false,
        "editor.snippetSuggestions": "none",
        "editor.formatOnSave": true,
        "editor.codeActionsOnSave": {
            "source.organizeImports": true
        }
    },
    "go.goroot": "F:\\Go", ///改成自己的goroot,即go所在文件夹
    //F:\Go_sourceCode
    "go.gopath": "F:\\Go_sourceCode", ///改成自己的gopath,即工作区
    "go.autocompleteUnimportedPackages": true,
    "go.docsTool": "gogetdoc",
    "gopls": {
        "usePlaceholders": true, // add parameter placeholders when completing a function
        "wantCompletionDocumentation": true // for documentation in completion items
    },
    "files.eol": "\n",
    "files.autoSave": "afterDelay", // formatting only supports LF line endings

一些终端命令

go mod init xxx 创建mod文件(xxx写项目名)

go mod tidy 自动下载所需包并删除多余的

我是先导入了之后课程所需的代码,输入god tidy之后会自动引入所需包,之后mod文件应该长这样

image.png

image.png

3.测试

test文件命名规则 xxx_test.go

 func TestMain(m *testing.M) {
 	//测试前:数据装载、配置初始化等前置工作
 	code := m.Run()
 	//测试后:释放资源等收尾工作
 	os.Exit(code)
 }

例子

package service

import (
	"testing"
)

func HelloTom() string {
	return "Jerry"
}
func TestHelloTom(t *testing.T) {
	outout := HelloTom()
	expectOutput := "Tom"
	if outout != expectOutput {
		t.Errorf("Expected %s do not match actual %s", expectOutput, outout)
	}
}

运行测试函数,得到结果

image.png

引用外部包

import (
	"testing"
	"github.com/stretchr/testify/assert"
)

func HelloTom() string {
	return "Jerry"
}
func TestHelloTom(t *testing.T) {
	outout := HelloTom()
	expectOutput := "Tom"
	// if outout != expectOutput {
	// 	t.Errorf("Expected %s do not match actual %s", expectOutput, outout)
	// }
	assert.Equal(t, expectOutput, outout)
}