✨ init
This commit is contained in:
commit
e6ee860fd9
29
.gitignore
vendored
Normal file
29
.gitignore
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
### Go template
|
||||||
|
# If you prefer the allow list template instead of the deny list, see community template:
|
||||||
|
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||||
|
#
|
||||||
|
# Binaries for programs and plugins
|
||||||
|
*.exe
|
||||||
|
*.exe~
|
||||||
|
*.dll
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
|
||||||
|
# Test binary, built with `go test -c`
|
||||||
|
*.test
|
||||||
|
|
||||||
|
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||||
|
*.out
|
||||||
|
|
||||||
|
# Dependency directories (remove the comment below to include it)
|
||||||
|
# vendor/
|
||||||
|
|
||||||
|
# Go workspace file
|
||||||
|
go.work
|
||||||
|
|
||||||
|
.idea
|
||||||
|
# .vscode
|
||||||
|
app.env
|
||||||
|
|
||||||
|
.DS_Store
|
||||||
|
*/.DS_Store
|
7
.vscode/settings.json
vendored
Normal file
7
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"go.autocompleteUnimportedPackages": true,
|
||||||
|
"go.testFlags": [
|
||||||
|
"-v",
|
||||||
|
"-cover"
|
||||||
|
]
|
||||||
|
}
|
73
README.MD
Normal file
73
README.MD
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
# Go学习
|
||||||
|
|
||||||
|
### 编写hello world程序
|
||||||
|
- package 必须是 main
|
||||||
|
- 方法名必须是 main()
|
||||||
|
- 文件名不一定是 main.go
|
||||||
|
|
||||||
|
### 和其他语言差异
|
||||||
|
- Go中 main 函数不支持任何返回值
|
||||||
|
- 通过os.Exit来返回状态
|
||||||
|
- main 函数不支持传入参数
|
||||||
|
- 通过 os.Args 获取命令行参数
|
||||||
|
|
||||||
|
### 编写测试程序
|
||||||
|
1. 源码文件以 `_test` 结尾: xxx_test.go
|
||||||
|
2. 测试方法名以 Test 开头: func TestXXX(t *testing.T) {...}
|
||||||
|
|
||||||
|
|
||||||
|
### 变量赋值
|
||||||
|
- 赋值可以自动进行类型推断
|
||||||
|
- 在一个赋值语句中可以对多个变量进行同时赋值
|
||||||
|
```go
|
||||||
|
// 单独赋值
|
||||||
|
var a int = 1
|
||||||
|
var b int = 1
|
||||||
|
|
||||||
|
// 同时赋值
|
||||||
|
var (
|
||||||
|
a int = 1
|
||||||
|
b int = 1
|
||||||
|
)
|
||||||
|
|
||||||
|
// 自动推断
|
||||||
|
a := 1
|
||||||
|
b := 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### 基本数据类型
|
||||||
|
|
||||||
|
```go
|
||||||
|
bool
|
||||||
|
string
|
||||||
|
int int8 int16 int32 int64
|
||||||
|
uint uint8 uint16 uint32 uint64 uintptr
|
||||||
|
byte // alias for uint8
|
||||||
|
rune // alias for int32, represents a Unicode code point
|
||||||
|
float32 float64
|
||||||
|
complex64 complex128
|
||||||
|
```
|
||||||
|
|
||||||
|
### 类型转化
|
||||||
|
- 与其他编程语言的差异
|
||||||
|
1. Go不允许隐式类型转换
|
||||||
|
2. 别名和原有类型也不能进行隐式类型转换
|
||||||
|
|
||||||
|
- 类型的预定义值
|
||||||
|
1. math.MaxInt64
|
||||||
|
2. math.MaxFloat64
|
||||||
|
3. math.MaxUint32
|
||||||
|
|
||||||
|
|
||||||
|
### 算术运算符
|
||||||
|
- Go语言没有前置的 ++, --,
|
||||||
|
|
||||||
|
|
||||||
|
### 线程和协程 Thread vs. Groutine
|
||||||
|
|
||||||
|
1. 创建时默认的 stack (栈) 的大小
|
||||||
|
- JDK5 以后 Java Thread stack 默认为 1M
|
||||||
|
- Groutine 的 stack 初始化大小为 2k
|
||||||
|
2. 和 KSE (Kernel Speace Entity) 的对应关系
|
||||||
|
- Java Thread 是 1:1
|
||||||
|
- Groutine 是 M:N
|
5
go.mod
Normal file
5
go.mod
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module go_study
|
||||||
|
|
||||||
|
go 1.20
|
||||||
|
|
||||||
|
require github.com/go-playground/assert/v2 v2.2.0
|
2
go.sum
Normal file
2
go.sum
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
60
src/c10_func/func_test.go
Normal file
60
src/c10_func/func_test.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package func_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 多返回值函数
|
||||||
|
func returnMultiValues() (int, int) {
|
||||||
|
return rand.Intn(10), rand.Intn(20)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 函数用法之一: 统计执行时间
|
||||||
|
func timeSpent(inner func(op int) int) func(op int) int {
|
||||||
|
return func(n int) int {
|
||||||
|
start := time.Now()
|
||||||
|
ret := inner(n)
|
||||||
|
fmt.Println("time spent: ", time.Since(start).Seconds())
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func slowFun(op int) int {
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
return op
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFn(t *testing.T) {
|
||||||
|
a, b := returnMultiValues()
|
||||||
|
t.Log(a, b)
|
||||||
|
|
||||||
|
tsSF := timeSpent(slowFun)
|
||||||
|
t.Log(tsSF(10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 求和
|
||||||
|
func Sum(ops ...int) int {
|
||||||
|
ret := 0
|
||||||
|
for _, op := range ops {
|
||||||
|
ret += op
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVarParam(t *testing.T) {
|
||||||
|
t.Log(Sum(1, 3, 5, 7, 9, 2, 6))
|
||||||
|
}
|
||||||
|
|
||||||
|
func Clear() {
|
||||||
|
fmt.Println("Clear resources")
|
||||||
|
}
|
||||||
|
|
||||||
|
// defer 延迟执行 一般用于资源的释放和异常的捕捉
|
||||||
|
func TestDefer(t *testing.T) {
|
||||||
|
defer Clear()
|
||||||
|
fmt.Println("Start")
|
||||||
|
panic("program error") //抛出异常
|
||||||
|
}
|
29
src/c11_customer_type/customer_type_test.go
Normal file
29
src/c11_customer_type/customer_type_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package customer_tpye_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 自定义类型
|
||||||
|
type IntConv func(op int) int
|
||||||
|
|
||||||
|
func timeSpent(inner IntConv) IntConv {
|
||||||
|
return func(n int) int {
|
||||||
|
start := time.Now()
|
||||||
|
ret := inner(n)
|
||||||
|
fmt.Println("time spent: ", time.Since(start).Seconds())
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func slowFun(op int) int {
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
return op
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFn(t *testing.T) {
|
||||||
|
tsSF := timeSpent(slowFun)
|
||||||
|
t.Log(tsSF(10))
|
||||||
|
}
|
48
src/c11_encapsulate/encap_test.go
Normal file
48
src/c11_encapsulate/encap_test.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package encap_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Employee struct {
|
||||||
|
Id string
|
||||||
|
Name string
|
||||||
|
Age int
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指针形式 复用 推荐使用
|
||||||
|
func (e *Employee) String() string {
|
||||||
|
fmt.Printf("Address is %x \n", unsafe.Pointer(&e.Name))
|
||||||
|
return fmt.Sprintf("Id: %s, Name: %s, Age: %d", e.Id, e.Name, e.Age)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数据复制, 地址变了
|
||||||
|
// func (e Employee) String() string {
|
||||||
|
// fmt.Printf("Address is %x \n", unsafe.Pointer(&e.Name))
|
||||||
|
// return fmt.Sprintf("Id: %s, Name: %s, Age: %d", e.Id, e.Name, e.Age)
|
||||||
|
// }
|
||||||
|
|
||||||
|
func TestCreateEmployeeObj(t *testing.T) {
|
||||||
|
e := Employee{"1", "Frank", 20}
|
||||||
|
e1 := Employee{Name: "Mike", Age: 28}
|
||||||
|
e2 := new(Employee) //返回指针
|
||||||
|
e2.Id = "3"
|
||||||
|
e2.Name = "Jack"
|
||||||
|
e2.Age = 32
|
||||||
|
|
||||||
|
t.Log(e)
|
||||||
|
t.Log(e1)
|
||||||
|
t.Log(e1.Id) //空字符串
|
||||||
|
t.Log(e2)
|
||||||
|
|
||||||
|
t.Logf("e is %T", e)
|
||||||
|
t.Logf("e2 is %T", e2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStructOperations(t *testing.T) {
|
||||||
|
e := Employee{"1", "Frank", 20}
|
||||||
|
fmt.Printf("Address is %x \n", unsafe.Pointer(&e.Name))
|
||||||
|
t.Log(e.String())
|
||||||
|
}
|
20
src/c11_interface/interface_test.go
Normal file
20
src/c11_interface/interface_test.go
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
package interface_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
type Programmer interface {
|
||||||
|
WriteWord() string
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoProgrammer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *GoProgrammer) WriteWord() string {
|
||||||
|
return "hello world"
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestClient(t *testing.T) {
|
||||||
|
var p Programmer
|
||||||
|
p = new(GoProgrammer)
|
||||||
|
t.Log(p.WriteWord())
|
||||||
|
}
|
68
src/c12_extend/extension_test.go
Normal file
68
src/c12_extend/extension_test.go
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package extension_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// golang是可以实现继承的,但是这种继承并不是严格意义上的继承,
|
||||||
|
// golang并不支持继承特性,因而也没有单继承,多继承,重写方法等复杂概念。
|
||||||
|
|
||||||
|
type Pet struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pet) Speak() {
|
||||||
|
fmt.Println("...")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Pet) SpeakTo(host string) {
|
||||||
|
p.Speak()
|
||||||
|
fmt.Println(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 有名继承
|
||||||
|
|
||||||
|
type Dog struct {
|
||||||
|
p *Pet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dog) Speak() {
|
||||||
|
fmt.Println("汪汪汪!")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDog(t *testing.T) {
|
||||||
|
dog := new(Dog)
|
||||||
|
dog.Speak()
|
||||||
|
// dog.SpeakTo("Zhang") Dog没有这个方法, 虽然Pet有,但是此时Dog不是匿名继承 ,如果要要调用需要指定继承事指定的名称。
|
||||||
|
dog.p.Speak()
|
||||||
|
dog.p.SpeakTo("Li")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 匿名继承
|
||||||
|
|
||||||
|
type People struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *People) ShowA() {
|
||||||
|
fmt.Println("AAA")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *People) ShowB() {
|
||||||
|
fmt.Println("BBB")
|
||||||
|
}
|
||||||
|
|
||||||
|
type Doctor struct {
|
||||||
|
People
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Doctor) ShowA() {
|
||||||
|
fmt.Println("Doctor AAA")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoctor(t *testing.T) {
|
||||||
|
d := new(Doctor)
|
||||||
|
d.ShowA()
|
||||||
|
d.ShowB() // 因为Teacher没有ShowB()的方法,此时又匿名继承了People,所以会调到People实现的ShowB方法
|
||||||
|
d.People.ShowA()
|
||||||
|
d.People.ShowB()
|
||||||
|
}
|
41
src/c12_polymorphism/polymorphism_test.go
Normal file
41
src/c12_polymorphism/polymorphism_test.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package polymorphism
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 多态
|
||||||
|
|
||||||
|
type Code string
|
||||||
|
|
||||||
|
type Programmer interface {
|
||||||
|
WriteHelloWorld() Code
|
||||||
|
}
|
||||||
|
|
||||||
|
type GoProgrammer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (gp *GoProgrammer) WriteHelloWorld() Code {
|
||||||
|
return "fmt.Println(\"hello world\")"
|
||||||
|
}
|
||||||
|
|
||||||
|
type JavaProgrammer struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (jp *JavaProgrammer) WriteHelloWorld() Code {
|
||||||
|
return "System.out.Println(\"hello world\")"
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeFirstProgram(p Programmer) {
|
||||||
|
// %T 输出类型
|
||||||
|
fmt.Printf("%T %v\n", p, p.WriteHelloWorld())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPolymorphism(t *testing.T) {
|
||||||
|
gp := new(GoProgrammer)
|
||||||
|
// gp := &GoProgrammer{}
|
||||||
|
jp := new(JavaProgrammer)
|
||||||
|
writeFirstProgram(gp)
|
||||||
|
writeFirstProgram(jp)
|
||||||
|
}
|
40
src/c13_empty_interface/empty_interface_test.go
Normal file
40
src/c13_empty_interface/empty_interface_test.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package empty_interface
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 空接口与断言
|
||||||
|
// 1.空接口可以表示任何类型
|
||||||
|
// 2.通过断言来将空接口转换为制定类型
|
||||||
|
|
||||||
|
// 空接口入参
|
||||||
|
func DoSomething(p interface{}) {
|
||||||
|
// if i, ok := p.(int); ok {
|
||||||
|
// fmt.Println("Integer", i)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if s, ok := p.(string); ok {
|
||||||
|
// fmt.Println("String", s)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fmt.Println("Unknown Type")
|
||||||
|
switch v := p.(type) {
|
||||||
|
case int:
|
||||||
|
fmt.Println("Integer", v)
|
||||||
|
case string:
|
||||||
|
fmt.Println("String", v)
|
||||||
|
default:
|
||||||
|
fmt.Println("Unknown Type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试空接口入参断言
|
||||||
|
func TestEmptyInterfaceAssertion(t *testing.T) {
|
||||||
|
DoSomething(10)
|
||||||
|
DoSomething("1231")
|
||||||
|
DoSomething(8.88)
|
||||||
|
}
|
45
src/c14_error_test/error_test.go
Normal file
45
src/c14_error_test/error_test.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package error_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Go的错误机制
|
||||||
|
1.没有异常机制
|
||||||
|
2.error类型实现了error接口
|
||||||
|
3.可以通过 errors.New 来快速创建错误实例
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
var ErrorLessThanTwo = errors.New("n should be not less than 2")
|
||||||
|
var ErrorLargerThenHundred = errors.New("n should be not larger than 100")
|
||||||
|
|
||||||
|
func GetFibonacci(n int) ([]int, error) {
|
||||||
|
if n < 2 {
|
||||||
|
return nil, ErrorLessThanTwo
|
||||||
|
}
|
||||||
|
if n > 100 {
|
||||||
|
return nil, ErrorLargerThenHundred
|
||||||
|
}
|
||||||
|
fibList := []int{1, 1}
|
||||||
|
for i := 2; i < n; i++ {
|
||||||
|
fibList = append(fibList, fibList[i-2]+fibList[i-1])
|
||||||
|
}
|
||||||
|
return fibList, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetFibonacci(t *testing.T) {
|
||||||
|
if ret, err := GetFibonacci(-10); err != nil {
|
||||||
|
if err == ErrorLessThanTwo {
|
||||||
|
fmt.Println("It is less.")
|
||||||
|
}
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
t.Log(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
34
src/c14_panic_recover/panic_recover_test.go
Normal file
34
src/c14_panic_recover/panic_recover_test.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package panic_recover
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Let it Crash 往往是恢复不确定性错误的最好方法
|
||||||
|
// panic 用于不可以恢复的错误
|
||||||
|
// panic 退出前会执行 defer 指定的内容
|
||||||
|
|
||||||
|
// os.Exit 退出时不会调用 defer 指定的函数
|
||||||
|
// os.Exit 退出时不输出当前调用栈信息
|
||||||
|
|
||||||
|
// 常见的错误恢复 recover()
|
||||||
|
// 当心 recover 成为"恶魔" 形成僵尸服务进程 导致health check失效
|
||||||
|
|
||||||
|
func TestPanicVxExit(t *testing.T) {
|
||||||
|
// defer func () {
|
||||||
|
// fmt.Println("Finally")
|
||||||
|
// }()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
// recover() 错误恢复 小心使用
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
fmt.Println("recovered from ", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
fmt.Println("Start")
|
||||||
|
panic(errors.New("Something wrong!"))
|
||||||
|
// os.Exit(-1)
|
||||||
|
// fmt.Println("end")
|
||||||
|
}
|
18
src/c15_package_test/package_test.go
Normal file
18
src/c15_package_test/package_test.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package client
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go_study/src/c15_package_test/series"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 可复用模块 包
|
||||||
|
// 基本复用单元 以首字母大写来表明可被包外代码访问
|
||||||
|
// 代码的 package 可以和所在的目录不一致
|
||||||
|
// 同一目录里的 Go 代码的 package 要保持一致
|
||||||
|
|
||||||
|
// 测试复用方法
|
||||||
|
func TestPackage(t *testing.T) {
|
||||||
|
t.Log(series.GetFibonacciSeries(5))
|
||||||
|
|
||||||
|
// t.Log(series.getSlice()) // 报错 无法访问
|
||||||
|
}
|
14
src/c15_package_test/series/my_series.go
Normal file
14
src/c15_package_test/series/my_series.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package series
|
||||||
|
|
||||||
|
// GetFibonacciSeries 实现 斐波那契数列
|
||||||
|
func GetFibonacciSeries(n int) []int {
|
||||||
|
fibList := []int{1, 1}
|
||||||
|
for i := 2; i < n; i++ {
|
||||||
|
fibList = append(fibList, fibList[i-2]+fibList[i-1])
|
||||||
|
}
|
||||||
|
return fibList
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSlice() []int {
|
||||||
|
return []int{1, 2, 3, 4, 5}
|
||||||
|
}
|
18
src/c16_groutine/groutine_test.go
Normal file
18
src/c16_groutine/groutine_test.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package groutine_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 测试协程机制
|
||||||
|
func TestGroutine(t *testing.T) {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
// 协程
|
||||||
|
go func(i int) {
|
||||||
|
fmt.Println(i)
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 50)
|
||||||
|
}
|
89
src/c17_share_memory/share_mem_test.go
Normal file
89
src/c17_share_memory/share_mem_test.go
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
package share_mem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 共享内存并发机制
|
||||||
|
|
||||||
|
func TestCounter(t *testing.T) {
|
||||||
|
counter := 0
|
||||||
|
for i := 0; i < 5000; i++ {
|
||||||
|
go func() {
|
||||||
|
// 不安全的写法
|
||||||
|
counter++
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
t.Logf("counter= %d", counter) // 得到的结果会小于 5000
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutex的使用
|
||||||
|
func TestCounterThread(t *testing.T) {
|
||||||
|
var mut sync.Mutex
|
||||||
|
counter := 0
|
||||||
|
for i := 0; i < 5000; i++ {
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
mut.Unlock()
|
||||||
|
}()
|
||||||
|
mut.Lock()
|
||||||
|
counter++
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second * 1) //去掉 time.Sleep(), counter的结果不符合预期, 小于 5000
|
||||||
|
t.Logf("counter= %d", counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 利用WaitGroup来确定所有任务是否结束
|
||||||
|
func TestCounterWaitGroup(t *testing.T) {
|
||||||
|
var mut sync.Mutex
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
counter := 0
|
||||||
|
for i := 0; i < 5000; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer func() {
|
||||||
|
mut.Unlock()
|
||||||
|
}()
|
||||||
|
mut.Lock()
|
||||||
|
counter++
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait() //挂起 等待完成
|
||||||
|
t.Logf("counter= %d", counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rwMutex *sync.RWMutex
|
||||||
|
var wg *sync.WaitGroup
|
||||||
|
|
||||||
|
// 读写锁
|
||||||
|
func TestRWMutex(t *testing.T) {
|
||||||
|
rwMutex = new(sync.RWMutex)
|
||||||
|
wg = new(sync.WaitGroup)
|
||||||
|
wg.Add(2)
|
||||||
|
go readData(1)
|
||||||
|
go readData(2)
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
fmt.Println("func finish")
|
||||||
|
}
|
||||||
|
|
||||||
|
func readData(i int) {
|
||||||
|
defer wg.Done()
|
||||||
|
fmt.Println(i, "start locking!")
|
||||||
|
// 给读操作 上锁
|
||||||
|
rwMutex.RLock()
|
||||||
|
// 读数据
|
||||||
|
fmt.Println(i, "Reading data")
|
||||||
|
// 休眠1s
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
// 读解锁
|
||||||
|
rwMutex.RUnlock()
|
||||||
|
// 打印提示信息
|
||||||
|
fmt.Println(i, "Read over")
|
||||||
|
}
|
41
src/c18_csp/async_service_test.go
Normal file
41
src/c18_csp/async_service_test.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
package csp_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func service() string {
|
||||||
|
time.Sleep(time.Millisecond * 50)
|
||||||
|
return "Done"
|
||||||
|
}
|
||||||
|
|
||||||
|
func otherTask() {
|
||||||
|
fmt.Println("执行一些其他的任务")
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
fmt.Println("任务完成")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestService(t *testing.T) {
|
||||||
|
fmt.Println(service())
|
||||||
|
otherTask()
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsyncService() chan string {
|
||||||
|
retChan := make(chan string, 1) // buffered channel (非阻塞、异步模式) 不会阻塞service
|
||||||
|
// retChan := make(chan string) // unbuffered channel (阻塞、同步模式) 会阻塞service
|
||||||
|
go func() {
|
||||||
|
ret := service()
|
||||||
|
fmt.Println("返回service结果")
|
||||||
|
retChan <- ret
|
||||||
|
fmt.Println("service已退出")
|
||||||
|
}()
|
||||||
|
return retChan
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAsyncService(t *testing.T) {
|
||||||
|
retCh := AsyncService()
|
||||||
|
otherTask()
|
||||||
|
fmt.Println(<-retCh)
|
||||||
|
}
|
34
src/c19_select/select_test.go
Normal file
34
src/c19_select/select_test.go
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
package select_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func service() string {
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
return "Done"
|
||||||
|
}
|
||||||
|
|
||||||
|
func AsyncService() chan string {
|
||||||
|
retChan := make(chan string, 1) // buffered channel (非阻塞、异步模式) 不会阻塞service
|
||||||
|
// retChan := make(chan string) // unbuffered channel (阻塞、同步模式) 会阻塞service
|
||||||
|
go func() {
|
||||||
|
ret := service()
|
||||||
|
fmt.Println("返回service结果")
|
||||||
|
retChan <- ret
|
||||||
|
fmt.Println("service已退出")
|
||||||
|
}()
|
||||||
|
return retChan
|
||||||
|
}
|
||||||
|
|
||||||
|
// 多路选择 select 实现超时机制
|
||||||
|
func TestSelect(t *testing.T) {
|
||||||
|
select {
|
||||||
|
case ret := <-AsyncService():
|
||||||
|
t.Log(ret)
|
||||||
|
case <-time.After(time.Millisecond * 100):
|
||||||
|
t.Error("超时")
|
||||||
|
}
|
||||||
|
}
|
19
src/c1_hello_world/hello_world.go
Normal file
19
src/c1_hello_world/hello_world.go
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Go中 main 函数不支持任何返回值
|
||||||
|
// main 函数不支持传入参数
|
||||||
|
func main() {
|
||||||
|
// 通过 os.Args 获取命令行参数
|
||||||
|
fmt.Println(os.Args)
|
||||||
|
fmt.Println("Hello Golang!")
|
||||||
|
os.Exit(0) // 通过os.Exit来返回状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// package 必须是 main
|
||||||
|
// 方法名必须是 main()
|
||||||
|
// 文件名不一定是 main.go
|
43
src/c20_cancel_by_close/cancel_test.go
Normal file
43
src/c20_cancel_by_close/cancel_test.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package cancel_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isCancelled(cancelChan chan struct{}) bool {
|
||||||
|
select {
|
||||||
|
case <-cancelChan:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cancel1(cancelChan chan struct{}) {
|
||||||
|
cancelChan <- struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cancel2(cancelChan chan struct{}) {
|
||||||
|
close(cancelChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用close来取消任务
|
||||||
|
func TestCancel(t *testing.T) {
|
||||||
|
cancelChan := make(chan struct{}, 0)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
go func(i int, cancelChan chan struct{}) {
|
||||||
|
for {
|
||||||
|
if isCancelled(cancelChan) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 5)
|
||||||
|
}
|
||||||
|
fmt.Println(i, "Done")
|
||||||
|
}(i, cancelChan)
|
||||||
|
}
|
||||||
|
// cancel1(cancelChan)
|
||||||
|
cancel2(cancelChan)
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
}
|
35
src/c20_cancel_by_context/cancel_by_context_test.go
Normal file
35
src/c20_cancel_by_context/cancel_by_context_test.go
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
package cancel_by_context_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isCancelled(ctx context.Context) bool {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用context来取消任务
|
||||||
|
func TestCancel(t *testing.T) {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
go func(i int, ctx context.Context) {
|
||||||
|
for {
|
||||||
|
if isCancelled(ctx) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(time.Millisecond * 5)
|
||||||
|
}
|
||||||
|
fmt.Println(i, "Done")
|
||||||
|
}(i, ctx)
|
||||||
|
}
|
||||||
|
cancel()
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
}
|
56
src/c20_channel_close/channel_close_test.go
Normal file
56
src/c20_channel_close/channel_close_test.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
package channel_close_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 生产者
|
||||||
|
func dataProducer(ch chan int, wg *sync.WaitGroup) {
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
ch <- i
|
||||||
|
}
|
||||||
|
close(ch) // 关闭channel
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 消费者
|
||||||
|
// func dataReceiver(ch chan int, wg *sync.WaitGroup) {
|
||||||
|
// go func() {
|
||||||
|
// for i := 0; i < 10; i++ {
|
||||||
|
// data := <-ch
|
||||||
|
// fmt.Println(data)
|
||||||
|
// }
|
||||||
|
// wg.Done()
|
||||||
|
// }()
|
||||||
|
// }
|
||||||
|
|
||||||
|
func dataReceiver(ch chan int, wg *sync.WaitGroup) {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
if data, ok := <-ch; ok {
|
||||||
|
fmt.Println(data)
|
||||||
|
} else {
|
||||||
|
// 通道关闭 去获取值会立即返回通道定义类型的默认值
|
||||||
|
fmt.Println(data)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCloseChannel(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
ch := make(chan int)
|
||||||
|
wg.Add(1)
|
||||||
|
dataProducer(ch, &wg)
|
||||||
|
wg.Add(1)
|
||||||
|
dataReceiver(ch, &wg)
|
||||||
|
wg.Add(1)
|
||||||
|
dataReceiver(ch, &wg)
|
||||||
|
wg.Wait()
|
||||||
|
}
|
37
src/c21_singleton/once_test.go
Normal file
37
src/c21_singleton/once_test.go
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
package once_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 利用sync.Once实现 单例模式
|
||||||
|
|
||||||
|
type Singleton struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var singletonInstance *Singleton
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
|
func GetSingletonObj() *Singleton {
|
||||||
|
once.Do(func() {
|
||||||
|
fmt.Println("创建对象")
|
||||||
|
singletonInstance = new(Singleton)
|
||||||
|
})
|
||||||
|
return singletonInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSingletonObj(t *testing.T) {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
obj := GetSingletonObj()
|
||||||
|
t.Logf("%x", unsafe.Pointer(obj))
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
33
src/c22_util_anyone_reply/first_response_test.go
Normal file
33
src/c22_util_anyone_reply/first_response_test.go
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
package util_anyone_reply
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runTask(id int) string {
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
return fmt.Sprintf("结果来自: %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FirstResponse() string {
|
||||||
|
numOfRunner := 10
|
||||||
|
// 使用 buffered channel (非阻塞、异步模式) 来防止协程泄露
|
||||||
|
ch := make(chan string, numOfRunner)
|
||||||
|
for i := 0; i < numOfRunner; i++ {
|
||||||
|
go func(i int) {
|
||||||
|
ret := runTask(i)
|
||||||
|
ch <- ret
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
return <-ch
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFirstResponse(t *testing.T) {
|
||||||
|
t.Log("Before 当前系统协程数: ", runtime.NumGoroutine())
|
||||||
|
t.Log(FirstResponse())
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
t.Log("After 当前协程数: ", runtime.NumGoroutine())
|
||||||
|
}
|
36
src/c23_util_all_done/all_done_test.go
Normal file
36
src/c23_util_all_done/all_done_test.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package util_all_done
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func runTask(id int) string {
|
||||||
|
time.Sleep(time.Millisecond * 10)
|
||||||
|
return fmt.Sprintf("结果来自: %d", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AllResponse() string {
|
||||||
|
numOfRunner := 10
|
||||||
|
ch := make(chan string, numOfRunner)
|
||||||
|
for i := 0; i < numOfRunner; i++ {
|
||||||
|
go func(i int) {
|
||||||
|
ret := runTask(i)
|
||||||
|
ch <- ret
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
finalRet := ""
|
||||||
|
for j := 0; j < numOfRunner; j++ {
|
||||||
|
finalRet += <-ch + "\n"
|
||||||
|
}
|
||||||
|
return finalRet
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllResponse(t *testing.T) {
|
||||||
|
t.Log("Before 当前系统协程数: ", runtime.NumGoroutine())
|
||||||
|
t.Log(AllResponse())
|
||||||
|
time.Sleep(time.Second * 1)
|
||||||
|
t.Log("After 当前协程数: ", runtime.NumGoroutine())
|
||||||
|
}
|
42
src/c24_obj_pool/obj_pool.go
Normal file
42
src/c24_obj_pool/obj_pool.go
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
package obj_pool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MyObj struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
type ObjPool struct {
|
||||||
|
buffChan chan *MyObj
|
||||||
|
}
|
||||||
|
|
||||||
|
// 利用channel实现的对象池
|
||||||
|
|
||||||
|
func NewObjPool(num int) *ObjPool {
|
||||||
|
objPool := ObjPool{}
|
||||||
|
objPool.buffChan = make(chan *MyObj, num)
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
objPool.buffChan <- &MyObj{}
|
||||||
|
}
|
||||||
|
return &objPool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ObjPool) GetObj(timeout time.Duration) (*MyObj, error) {
|
||||||
|
select {
|
||||||
|
case ret := <-p.buffChan:
|
||||||
|
return ret, nil
|
||||||
|
case <-time.After(timeout): // 超时控制
|
||||||
|
return nil, errors.New("超时")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ObjPool) PutObj(obj *MyObj) error {
|
||||||
|
select {
|
||||||
|
case p.buffChan <- obj:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return errors.New("超出对象池最大容量")
|
||||||
|
}
|
||||||
|
}
|
27
src/c24_obj_pool/obj_pool_test.go
Normal file
27
src/c24_obj_pool/obj_pool_test.go
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
package obj_pool
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestObjPool(t *testing.T) {
|
||||||
|
num := 10
|
||||||
|
pool := NewObjPool(num)
|
||||||
|
//if err := pool.PutObj(&MyObj{}); err != nil { // 尝试防止超出池大小的对象
|
||||||
|
// t.Error(err)
|
||||||
|
//}
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
|
if obj, err := pool.GetObj(time.Second * 1); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%T\n", obj)
|
||||||
|
if err := pool.PutObj(obj); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Log("Done.")
|
||||||
|
}
|
22
src/c25_obj_cache/README.MD
Normal file
22
src/c25_obj_cache/README.MD
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
### sync.Pool 对象获取
|
||||||
|
|
||||||
|
- 尝试从私有对象获取
|
||||||
|
- 私有对象不存在, 尝试从当前Processor的共享池获取
|
||||||
|
- 如果当前Processor共享池也是空的,那么就尝试去其他Processor的共享池获取
|
||||||
|
- 如果所有子池都是空的, 最后就用用户指定的New函数产生一个新的对象返回
|
||||||
|
|
||||||
|
### sync.Pool 对象的生命周期
|
||||||
|
|
||||||
|
- GC会清除sync.pool缓存的对象
|
||||||
|
- 对象的缓存有效期为下一次GC之前
|
||||||
|
|
||||||
|
### sync.Pool对象的放回
|
||||||
|
|
||||||
|
- 如果私有对象不存在则保存为私有对象
|
||||||
|
- 如果私有对象存在, 放入当前Processor子池的共享池中
|
||||||
|
|
||||||
|
### sync.Pool总结
|
||||||
|
|
||||||
|
- 适合于通过复用, 降低复杂对象的创建和GC代价
|
||||||
|
- 协程安全, 会有锁的开销
|
||||||
|
- 生命周期受GC影响, 不适合于做连接池等, 需自己管理生命周期的资源的池化
|
51
src/c25_obj_cache/sync_pool_test.go
Normal file
51
src/c25_obj_cache/sync_pool_test.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package obj_cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSyncPool(t *testing.T) {
|
||||||
|
pool := &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
t.Log("Create a new object.")
|
||||||
|
return 100
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
v := pool.Get().(int)
|
||||||
|
t.Log(v)
|
||||||
|
pool.Put(3)
|
||||||
|
|
||||||
|
v1 := pool.Get().(int)
|
||||||
|
t.Log("put操作后: ", v1)
|
||||||
|
runtime.GC() // GC 会清除sync.pool中缓存的对象
|
||||||
|
v2, _ := pool.Get().(int)
|
||||||
|
t.Log("GC操作后: ", v2)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 协程测试
|
||||||
|
func TestSyncPoolInMultiGroutine(t *testing.T) {
|
||||||
|
pool := &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
t.Log("create object")
|
||||||
|
return 10
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.Put(100)
|
||||||
|
pool.Put(100)
|
||||||
|
pool.Put(100)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func(id int) {
|
||||||
|
t.Log("从pool获取对象: ", pool.Get())
|
||||||
|
wg.Done()
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
5
src/c26_unit_test/functions.go
Normal file
5
src/c26_unit_test/functions.go
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
package unit_test
|
||||||
|
|
||||||
|
func Square(i int) int {
|
||||||
|
return i * i
|
||||||
|
}
|
46
src/c26_unit_test/functions_test.go
Normal file
46
src/c26_unit_test/functions_test.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
package unit_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/go-playground/assert/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 表格测试法
|
||||||
|
func TestSquare(t *testing.T) {
|
||||||
|
// 输入值
|
||||||
|
inputs := [...]int{1, 2, 3}
|
||||||
|
// 期待值
|
||||||
|
expected := [...]int{1, 4, 9}
|
||||||
|
for i := 0; i < len(inputs); i++ {
|
||||||
|
ret := Square(inputs[i])
|
||||||
|
if ret != expected[i] {
|
||||||
|
t.Errorf("input is %d, the expected is %d, the actual %d", inputs[i], expected[i], ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErrorInCode(t *testing.T) {
|
||||||
|
fmt.Println("Start")
|
||||||
|
t.Error("Error") // 程序继续执行
|
||||||
|
fmt.Println("End")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFatalInCode(t *testing.T) {
|
||||||
|
fmt.Println("Start")
|
||||||
|
t.Fatal("Error") // 程序会中断 不会输出End
|
||||||
|
fmt.Println("End")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用断言Assert测试
|
||||||
|
func TestSquareWithAssert(t *testing.T) {
|
||||||
|
// 输入值
|
||||||
|
inputs := [...]int{1, 2, 3}
|
||||||
|
// 期待值
|
||||||
|
expected := [...]int{1, 4, 9}
|
||||||
|
for i := 0; i < len(inputs); i++ {
|
||||||
|
ret := Square(inputs[i])
|
||||||
|
assert.Equal(t, expected[i], ret)
|
||||||
|
}
|
||||||
|
}
|
36
src/c2_const_test/constant_test.go
Normal file
36
src/c2_const_test/constant_test.go
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
package constant_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// 声明常量
|
||||||
|
const (
|
||||||
|
// 连续常量写法 借助 iota
|
||||||
|
Monday = iota + 1
|
||||||
|
Tuesday
|
||||||
|
Wednesday
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 位运算
|
||||||
|
Readable = 1 << iota
|
||||||
|
Writeable
|
||||||
|
Executable
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Cat = 1
|
||||||
|
Dog = 2
|
||||||
|
Pig = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConstant(t *testing.T) {
|
||||||
|
t.Log(Monday, Tuesday)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConstant1(t *testing.T) {
|
||||||
|
a := 1
|
||||||
|
t.Log(a&Readable, a&Writeable, a&Executable)
|
||||||
|
|
||||||
|
t.Log(a&Readable == Readable, a&Writeable == Writeable, a&Executable == Executable)
|
||||||
|
|
||||||
|
}
|
47
src/c2_const_test/fib_test/fib_test.go
Normal file
47
src/c2_const_test/fib_test/fib_test.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package fib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFibList(t *testing.T) {
|
||||||
|
// var a int = 1
|
||||||
|
// var b int = 1
|
||||||
|
|
||||||
|
// var (
|
||||||
|
// a int = 1
|
||||||
|
// b int = 1
|
||||||
|
// )
|
||||||
|
|
||||||
|
a := 1
|
||||||
|
b := 1
|
||||||
|
t.Log(a)
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
t.Log(b)
|
||||||
|
tmp := a
|
||||||
|
a = b
|
||||||
|
b = tmp + a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestExchange(t *testing.T) {
|
||||||
|
// 其他编程语言常见写法 借助中间变量tmp
|
||||||
|
a := 1
|
||||||
|
b := 2
|
||||||
|
tmp := a
|
||||||
|
a = b
|
||||||
|
b = tmp
|
||||||
|
t.Log(a, b)
|
||||||
|
|
||||||
|
// go 值替换
|
||||||
|
a, b = b, a
|
||||||
|
t.Log(a, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDemo(t *testing.T) {
|
||||||
|
a := "123"
|
||||||
|
b := "321"
|
||||||
|
t.Log(&a, &b)
|
||||||
|
a, b = b, a
|
||||||
|
t.Log(&a, &b, a, b)
|
||||||
|
}
|
9
src/c2_const_test/first_test.go
Normal file
9
src/c2_const_test/first_test.go
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
package constant_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFirst(t *testing.T) {
|
||||||
|
t.Log("我的第一个go测试程序!")
|
||||||
|
}
|
39
src/c3_type_test/type_test.go
Normal file
39
src/c3_type_test/type_test.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package tpye_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 自定义类型
|
||||||
|
type MyInt int64
|
||||||
|
|
||||||
|
// 类型转换
|
||||||
|
func TestImplicit(t *testing.T) {
|
||||||
|
var a int32 = 1
|
||||||
|
// 显示类型转换
|
||||||
|
var b = int64(a)
|
||||||
|
var c = MyInt(b)
|
||||||
|
t.Log(a, b, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 指针
|
||||||
|
func TestPoint(t *testing.T) {
|
||||||
|
a := 1
|
||||||
|
aPtr := &a
|
||||||
|
t.Log(a, aPtr)
|
||||||
|
t.Logf("%T %T", a, aPtr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestString(t *testing.T) {
|
||||||
|
// 不赋值默认空字符串
|
||||||
|
var s string
|
||||||
|
t.Log("*" + s + "*")
|
||||||
|
t.Log(len(s))
|
||||||
|
t.Log(s == "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMax(t *testing.T) {
|
||||||
|
t.Log(math.MaxFloat64)
|
||||||
|
t.Log(math.MaxInt64)
|
||||||
|
}
|
30
src/c4_operator_test/operator_test.go
Normal file
30
src/c4_operator_test/operator_test.go
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package operator_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// 数组比较
|
||||||
|
func TestCompareArray(t *testing.T) {
|
||||||
|
a := [...]int{1, 2, 3, 4}
|
||||||
|
b := [...]int{1, 2, 3, 5}
|
||||||
|
d := [...]int{1, 2, 3, 4}
|
||||||
|
t.Log(a == b)
|
||||||
|
//c := [...]int{1, 2, 3, 4, 5}
|
||||||
|
//t.Log(a == c) //报错 长度不同不能比较
|
||||||
|
t.Log(a == d)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
// 位运算
|
||||||
|
Readable = 1 << iota
|
||||||
|
Writeable
|
||||||
|
Executable
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBitClear(t *testing.T) {
|
||||||
|
a := 7
|
||||||
|
a = a &^ Readable
|
||||||
|
a = a &^ Executable
|
||||||
|
t.Log(a&Readable, a&Writeable, a&Executable)
|
||||||
|
t.Log(a&Readable == Readable, a&Writeable == Writeable, a&Executable == Executable)
|
||||||
|
}
|
40
src/c5_condition/condition_test.go
Normal file
40
src/c5_condition/condition_test.go
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
package condition_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// if
|
||||||
|
func TestIfMultiSec(t *testing.T) {
|
||||||
|
if a := 1 == 1.0; a {
|
||||||
|
t.Log("1 == 1")
|
||||||
|
} else {
|
||||||
|
t.Log("!=")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch
|
||||||
|
func TestSwitchMultiCase(t *testing.T) {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
switch i {
|
||||||
|
case 0, 2:
|
||||||
|
t.Log("偶数Even")
|
||||||
|
case 1, 3:
|
||||||
|
t.Log("基数Odd")
|
||||||
|
default:
|
||||||
|
t.Log("it is not 0-3")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch2
|
||||||
|
func TestSwitchCaseCondition(t *testing.T) {
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
switch {
|
||||||
|
case i%2 == 0:
|
||||||
|
t.Log("偶数Even")
|
||||||
|
case i%2 == 1:
|
||||||
|
t.Log("基数Odd")
|
||||||
|
default:
|
||||||
|
t.Log("it is default case")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
39
src/c6_array_test/array_test.go
Normal file
39
src/c6_array_test/array_test.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
package array_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// 数组声明
|
||||||
|
func TestArrayInit(t *testing.T) {
|
||||||
|
var arr [3]int
|
||||||
|
arr1 := [4]int{1, 2, 3, 4}
|
||||||
|
arr3 := [...]int{1, 3, 4, 5}
|
||||||
|
arr1[1] = 5
|
||||||
|
t.Log(arr[1], arr[2])
|
||||||
|
t.Log(arr1, arr3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数组遍历
|
||||||
|
func TestArrayTravel(t *testing.T) {
|
||||||
|
arr3 := [...]int{1, 3, 4, 5}
|
||||||
|
for i := 0; i < len(arr3); i++ {
|
||||||
|
t.Log(arr3[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, v := range arr3 {
|
||||||
|
t.Log(idx, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range arr3 {
|
||||||
|
t.Log(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 数组截断
|
||||||
|
func TestArraySection(t *testing.T) {
|
||||||
|
arr3 := [...]int{1, 2, 3, 4, 5}
|
||||||
|
// 截取 arr[开始索引(包含), 结束索引(不包含)]
|
||||||
|
arr3_sec := arr3[1:4]
|
||||||
|
t.Log(arr3_sec) //2.3.4
|
||||||
|
t.Log(arr3[1:]) //2,3,4,5
|
||||||
|
t.Log(arr3[:3]) //1,2,3
|
||||||
|
}
|
12
src/c6_loop/loop_test.go
Normal file
12
src/c6_loop/loop_test.go
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package loop_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// for循环
|
||||||
|
func TestWhileLoop(t *testing.T) {
|
||||||
|
n := 0
|
||||||
|
for n < 5 {
|
||||||
|
t.Log(n)
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
51
src/c6_slice_test/slice_test.go
Normal file
51
src/c6_slice_test/slice_test.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package slice_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
// 切片定义
|
||||||
|
func TestSlienceInit(t *testing.T) {
|
||||||
|
var s0 []int
|
||||||
|
t.Log(len(s0), cap(s0))
|
||||||
|
s0 = append(s0, 1)
|
||||||
|
t.Log(len(s0), cap(s0))
|
||||||
|
|
||||||
|
s1 := []int{1, 2, 3, 4}
|
||||||
|
t.Log(len(s1), cap(s1))
|
||||||
|
|
||||||
|
s2 := make([]int, 3, 5)
|
||||||
|
t.Log(len(s2), cap(s2))
|
||||||
|
t.Log(s2[0], s2[1], s2[2])
|
||||||
|
s2 = append(s2, 4)
|
||||||
|
t.Log(len(s2), cap(s2))
|
||||||
|
t.Log(s2[0], s2[1], s2[2], s2[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切片增长演示
|
||||||
|
func TestSliceGrowing(t *testing.T) {
|
||||||
|
s := []int{}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
s = append(s, i)
|
||||||
|
t.Log(len(s), cap(s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切片的共享内存 演示
|
||||||
|
func TestSliceShareMemory(t *testing.T) {
|
||||||
|
letter := []string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "L"}
|
||||||
|
letter2 := letter[3:6]
|
||||||
|
t.Log(letter2, len(letter2), cap(letter2))
|
||||||
|
l3 := letter[5:8]
|
||||||
|
t.Log(l3, len(l3), cap(l3))
|
||||||
|
l3[0] = "U"
|
||||||
|
t.Log(letter2)
|
||||||
|
t.Log(letter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切片不能比较
|
||||||
|
func TestSliceCompare(t *testing.T) {
|
||||||
|
// a := []int{1, 2, 3, 4}
|
||||||
|
// b := []int{1, 2, 3, 4}
|
||||||
|
// if a == b {
|
||||||
|
// t.Log("equals")
|
||||||
|
// }
|
||||||
|
}
|
50
src/c7_map_test/map_test.go
Normal file
50
src/c7_map_test/map_test.go
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
package map_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// map的声明
|
||||||
|
func TestMapInit(t *testing.T) {
|
||||||
|
m1 := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||||
|
t.Log(m1["b"])
|
||||||
|
t.Logf("len m1=%d", len(m1))
|
||||||
|
|
||||||
|
m2 := map[string]int{}
|
||||||
|
m2["d"] = 4
|
||||||
|
t.Logf("len m2=%d", len(m2))
|
||||||
|
m3 := make(map[string]int, 10)
|
||||||
|
t.Logf("len m3=%d", len(m3))
|
||||||
|
}
|
||||||
|
|
||||||
|
// map判断键key是否存在
|
||||||
|
func TestAccessNotExistingKey(t *testing.T) {
|
||||||
|
m1 := map[int]int{}
|
||||||
|
// 不存在会默认为0 而不是nil
|
||||||
|
t.Log(m1[1])
|
||||||
|
m1[2] = 0
|
||||||
|
t.Log(m1[2])
|
||||||
|
|
||||||
|
if v, ok := m1[3]; ok {
|
||||||
|
t.Logf("Key 3 value is %d", v)
|
||||||
|
} else {
|
||||||
|
t.Log("Key 3 is not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
// 赋值后
|
||||||
|
m1[3] = 0
|
||||||
|
if v, ok := m1[3]; ok {
|
||||||
|
t.Logf("Key 3 value is %d", v)
|
||||||
|
} else {
|
||||||
|
t.Log("Key 3 is not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历Map
|
||||||
|
func TestTravelMap(t *testing.T) {
|
||||||
|
m1 := map[string]int{"a": 1, "b": 2, "c": 3}
|
||||||
|
for k, v := range m1 {
|
||||||
|
t.Logf("Key: %s, Value: %d", k, v)
|
||||||
|
}
|
||||||
|
}
|
48
src/c8_map_ext/map_ext_test.go
Normal file
48
src/c8_map_ext/map_ext_test.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package map_ext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 值为方法的map
|
||||||
|
func TestMapWithFunValue(t *testing.T) {
|
||||||
|
m := map[int]func(op int) int{}
|
||||||
|
m[1] = func(op int) int { return op }
|
||||||
|
m[2] = func(op int) int { return op * op }
|
||||||
|
m[3] = func(op int) int { return op + op + op }
|
||||||
|
t.Log(m[1](2), m[2](2), m[3](2))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map实现Set操作
|
||||||
|
func TestMapForSet(t *testing.T) {
|
||||||
|
mySet := map[int]bool{}
|
||||||
|
|
||||||
|
// 添加元素
|
||||||
|
mySet[1] = true
|
||||||
|
mySet[2] = false
|
||||||
|
t.Logf("set len=%d", len(mySet))
|
||||||
|
|
||||||
|
// 判断元素是否存在
|
||||||
|
n := 3
|
||||||
|
if mySet[n] {
|
||||||
|
t.Logf("%d is exist", n)
|
||||||
|
} else {
|
||||||
|
t.Logf("%d is not exist", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
//删除元素
|
||||||
|
delete(mySet, 1)
|
||||||
|
ValueIsExit(mySet, 1)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValueIsExit(s map[int]bool, v int) bool {
|
||||||
|
if s[v] {
|
||||||
|
fmt.Printf("%d is exist", v)
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%d is not exist", v)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
31
src/c9_string/string_func_test.go
Normal file
31
src/c9_string/string_func_test.go
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
package string_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStringFn(t *testing.T) {
|
||||||
|
s := "A,B,C"
|
||||||
|
// 切割
|
||||||
|
parts := strings.Split(s, ",")
|
||||||
|
for _, v := range parts {
|
||||||
|
t.Log(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 连接
|
||||||
|
t.Log(strings.Join(parts, "-"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStrConv(t *testing.T) {
|
||||||
|
// int to string
|
||||||
|
s := strconv.Itoa(10)
|
||||||
|
t.Log(s)
|
||||||
|
|
||||||
|
// string to int
|
||||||
|
if i, err := strconv.Atoi("10"); err == nil {
|
||||||
|
t.Log(10 + i)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
src/c9_string/string_test.go
Normal file
29
src/c9_string/string_test.go
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
package string_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestString(t *testing.T) {
|
||||||
|
var s string
|
||||||
|
t.Log(s) //初始化值为空字符串 ""
|
||||||
|
s = "hello"
|
||||||
|
t.Log(len(s))
|
||||||
|
|
||||||
|
s = "\xE4\xB8\xA5" // 可以存储任何二进制数据
|
||||||
|
// s = "\xE4\xBA\xB5\xFF"
|
||||||
|
t.Log(s)
|
||||||
|
|
||||||
|
s = "中"
|
||||||
|
t.Log(len(s)) // 是byte数
|
||||||
|
|
||||||
|
c := []rune(s)
|
||||||
|
t.Log(len(c))
|
||||||
|
t.Logf("中 unicode %x", c[0])
|
||||||
|
t.Logf("中 UTF8 %x", s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringToRune(t *testing.T) {
|
||||||
|
s := "中华人民共和国"
|
||||||
|
for _, c := range s {
|
||||||
|
t.Logf("%[1]c %[1]d, %[1]x", c)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user