Go 语言从入门到实战
编写第一个 Go 程序
下载和安装
最新版:https://go.dev/doc/install
以往版本:https://golang.google.cn/dl/
🌟项目名:go_learning
基本程序结构
⭐src\ch1\main
hello_world.go
:
1 | package main |
获取退出返回值:os.Exit
获取命名行参数:os.Args
测试程序要求:
- 测试文件:
xxx_test.go
- 测试方法:
func TestXXX(t *testing.T){...}
⭐src\ch2\test
1⃣first_test.go
:
1 | package try_test |
=== RUN TestFirstTry
h:\go_learning\src\ch2\test\first_test.go:6: My first try!
— PASS: TestFirstTry (0.00s)
PASS
ok cs.liangjiajia.com/ch2/test (cached)
2⃣second_test.go
:
实现 数列:,,,,,
1 | package try_test |
=== RUN TestSecond
1 1 2 3 5 8 13 21 34 55 89
— PASS: TestFibonacci (0.00s)
PASS
ok cs.liangjiajia.com/ch2/test 1.428s
📌其他变量命名方法:
var x int = a
var (x int = a y int = b ...)
var (x = a y = b ...)
var a int
+a = 1
3⃣third_test.go
:
实现两数交换:
1 | package try_test |
=== RUN TestThirdTry
h:\go_learning\src\ch2\test\third_test.go:8: 1 2
h:\go_learning\src\ch2\test\third_test.go:10: 2 1
— PASS: TestThirdTry (0.00s)
PASS
ok cs.liangjiajia.com/ch2/test 1.492s
4⃣fourth_test.go
:
1 | package try_test |
=== RUN TestFourth
h:\go_learning\src\ch2\test\fourth_test.go:16: 0 1 2 3 4 5 6
— PASS: TestFourth (0.00s)
PASS
ok cs.liangjiajia.com/ch2/test 1.474s
5⃣fifth_test.go
:
1 | package try_test |
Readable
:1 << 0,即 ,Writable
:1 << 1,即 ,Executable
:1 << 2,即 ,
=== RUN TestFifth
h:\go_learning\src\ch2\test\fifth_test.go:12: 1 2 4
h:\go_learning\src\ch2\test\fifth_test.go:15: true true true
h:\go_learning\src\ch2\test\fifth_test.go:16: true true false
— PASS: TestFifth (0.00s)
PASS
ok cs.liangjiajia.com/ch2/test 1.418s
基本数据类型
⭐src\ch3\type
-
布尔型:
bool
-
字符串类型:
string
-
数字类型:
-
int
(32位或64位)、int8
、int16
、int32
、int64
-
uint
(32位或64位)、uint8
、uint16
、uint32
、uint64
、unitptr
(无符号整型,用于存放一个指针)
-
-
浮点型:
float32
、float64
complex64
、complex128
-
byte
:类似unit8
-
rune
:类似int32
语言不支持隐式类型转换
type_test.go
:
1 | func TestImplicit(t *testing.T) { |
=== RUN TestImplicit
h:\go_learning\src\ch3\type\type_test.go:11: 1 1
— PASS: TestImplicit (0.00s)
PASS
ok cs.liangjiajia.com/ch3/type 1.431s
不支持指针运算,string
类型初始化为空字符串。
1 | func TestPoint(t *testing.T) { |
=== RUN TestPoint
h:\go_learning\src\ch3\type\type_test.go:17: int *int
— PASS: TestPoint (0.00s)
PASS
ok cs.liangjiajia.com/ch3/type 1.504s
1 | func TestString(t *testing.T) { |
=== RUN TestString
h:\go_learning\src\ch3\type\type_test.go:22: **
h:\go_learning\src\ch3\type\type_test.go:23: 0
— PASS: TestString (0.00s)
PASS
ok cs.liangjiajia.com/ch3/type 1.600s
运算符
⭐src\ch4\operator
算术运算符
运算符 | 描述 |
---|---|
+ |
相加 |
- |
相减 |
* |
相乘 |
/ |
相除 |
% |
求余 |
++ |
自增 |
-- |
自减 |
比较运算符
运算符 | 描述 |
---|---|
== |
检查两个值是否相等 |
!= |
检查两个值是否不相等 |
> |
检查左边值是否大于右边值 |
< |
检查左边值是否小于右边值 |
>= |
检查左边值是否大于等于右边值 |
<= |
检查左边值是否小于等于右边值 |
用
==
比较数组,维度相同,对应元素相同
operator_test.go
:
1 | func TestCompareArray(t *testing.T) { |
=== RUN TestCompareArray
h:\go_learning\src\ch4\operator\operator_test.go:10: false
h:\go_learning\src\ch4\operator\operator_test.go:11: true
— PASS: TestCompareArray (0.00s)
PASS
ok cs.liangjiajia.com/ch4/operator 1.485s
逻辑运算符
运算符 | 描述 |
---|---|
&& |
逻辑 运算符 |
|| |
逻辑 运算符 |
! |
逻辑 运算符 |
位运算符
运算符 | 描述 |
---|---|
& |
按位与运算符 |
| |
按位或运算符 |
^ |
按位异或运算符 |
<< |
左移运算符 |
>> |
右移运算符 |
&^
:按位置零
1 &^ 0
1
0 &^ 0
0
1 &^ 1
0
0 &^ 1
0
operator_test.go
:
1 | const ( |
=== RUN TestBitClear
h:\go_learning\src\ch4\operator\operator_test.go:22: 1 2 4
h:\go_learning\src\ch4\operator\operator_test.go:24: true true true
h:\go_learning\src\ch4\operator\operator_test.go:26: false true true
h:\go_learning\src\ch4\operator\operator_test.go:28: false true false
— PASS: TestBitClear (0.00s)
PASS
ok
条件语句
if
条件语句:
1 | if condition { |
if...else...
条件语句:
1 | if condition { |
switch
条件语句:
1 | switch condition { |
select
条件语句:
1 | select { |
⭐src\ch5\condition</i>
condition_test.go
:
1 | // if |
=== RUN TestIfCondition
h:\go_learning\src\ch5\condition\condition_test.go:9: Even
h:\go_learning\src\ch5\condition\condition_test.go:11: Odd
h:\go_learning\src\ch5\condition\condition_test.go:9: Even
h:\go_learning\src\ch5\condition\condition_test.go:11: Odd
h:\go_learning\src\ch5\condition\condition_test.go:9: Even
— PASS: TestIfCondition (0.00s)
PASS
ok cs.liangjiajia.com/ch5/condition (cached)
condition_test.go
:
1 | // if-else |
=== RUN TestIfElseCondition
h:\go_learning\src\ch5\condition\condition_test.go:20: Even
h:\go_learning\src\ch5\condition\condition_test.go:23: Odd
h:\go_learning\src\ch5\condition\condition_test.go:20: Even
h:\go_learning\src\ch5\condition\condition_test.go:23: Odd
h:\go_learning\src\ch5\condition\condition_test.go:20: Even
— PASS: TestIfElseCondition (0.00s)
PASS
ok cs.liangjiajia.com/ch5/condition (cached)
condition_test.go
:
1 | func TestSwitchCaseCondition(t *testing.T) { |
=== RUN TestSwitchCaseCondition
h:\go_learning\src\ch5\condition\condition_test.go:36: Even
h:\go_learning\src\ch5\condition\condition_test.go:38: Odd
h:\go_learning\src\ch5\condition\condition_test.go:36: Even
h:\go_learning\src\ch5\condition\condition_test.go:38: Odd
h:\go_learning\src\ch5\condition\condition_test.go:36: Even
— PASS: TestSwitchCaseCondition (0.00s)
PASS
ok cs.liangjiajia.com/ch5/condition 0.038s
condition_test.go
:
1 | func TestSwitchMultiCaseCondition(t *testing.T) { |
=== RUN TestSwitchMultiCaseCondition
h:\go_learning\src\ch5\condition\condition_test.go:49: Even
h:\go_learning\src\ch5\condition\condition_test.go:51: Odd
h:\go_learning\src\ch5\condition\condition_test.go:49: Even
h:\go_learning\src\ch5\condition\condition_test.go:51: Odd
h:\go_learning\src\ch5\condition\condition_test.go:49: Even
— PASS: TestSwitchMultiCaseCondition (0.00s)
PASS
ok cs.liangjiajia.com/ch5/condition 0.039s
循环语句
for
循环
1 | for init; condition; post { |
类while
循环
1 | for condition { |
init
: 一般为赋值表达式,给控制变量赋初值;condition
: 关系表达式或逻辑表达式,循环控制条件;post
: 一般为赋值表达式,给控制变量增量或减量。
⭐src\ch5\loop
loop_test.go
:
1 | func TestWhileLoop(t *testing.T) { |
=== RUN TestWhileLoop
h:\go_learning\src\ch5\loop\loop_test.go:8: 0
h:\go_learning\src\ch5\loop\loop_test.go:8: 1
h:\go_learning\src\ch5\loop\loop_test.go:8: 2
h:\go_learning\src\ch5\loop\loop_test.go:8: 3
h:\go_learning\src\ch5\loop\loop_test.go:8: 4
— PASS: TestWhileLoop (0.00s)
PASS
ok cs.liangjiajia.com/ch5/loop 1.522s
loop_test.go
:
1 | func TestForLoop(t *testing.T) { |
=== RUN TestForLoop
h:\go_learning\src\ch5\loop\loop_test.go:15: 0
h:\go_learning\src\ch5\loop\loop_test.go:15: 1
h:\go_learning\src\ch5\loop\loop_test.go:15: 2
h:\go_learning\src\ch5\loop\loop_test.go:15: 3
h:\go_learning\src\ch5\loop\loop_test.go:15: 4
— PASS: TestForLoop (0.00s)
PASS
ok cs.liangjiajia.com/ch5/loop 0.039s
常用集合
数组
数组声明
1 | var arrayName [size]dataType |
1 | // 数组声明 |
=== RUN TestArrayDeclare
h:\go_learning\src\ch6\array\array_test.go:8: [0 0 0]
— PASS: TestArrayDeclare (0.00s)
PASS
ok cs.liangjiajia.com/ch6/array 1.567s
数组初始化
- 指定数组长度
1 | var arrayName [size]dataType{element_1,element_2,...,element_size-1} |
1 | arrayName := [size]dataType{element_1,element_2,...,element_size-1} |
- 不指定数组长度,自动推断
1 | var arrayName [...]dataType{element_1,element_2,...} |
1 | arrayName := [...]dataType{element_1,element_2,...} |
⭐src\ch6\array
array_test.go
:
1 | // 数组初始化 |
=== RUN TestArrayInit
h:\go_learning\src\ch6\array\array_test.go:14: [1 2 3]
h:\go_learning\src\ch6\array\array_test.go:17: [1 2 3]
— PASS: TestArrayInit (0.00s)
PASS
ok cs.liangjiajia.com/ch6/array 0.040s
关于数组遍历,数组切片,数组扩容
array_test.go
:
1 | // 数组遍历 |
=== RUN TestArrayTravel
h:\go_learning\src\ch6\array\array_test.go:24: 1
h:\go_learning\src\ch6\array\array_test.go:24: 2
h:\go_learning\src\ch6\array\array_test.go:24: 3
h:\go_learning\src\ch6\array\array_test.go:27: 1
h:\go_learning\src\ch6\array\array_test.go:27: 2
h:\go_learning\src\ch6\array\array_test.go:27: 3
— PASS: TestArrayTravel (0.00s)
PASS
ok cs.liangjiajia.com/ch6/array 0.039s
array_test.go
:
1 | // 数组切片 |
=== RUN TestArraySection
h:\go_learning\src\ch6\array\array_test.go:36: [1 2] [4 5]
— PASS: TestArraySection (0.00s)
PASS
ok cs.liangjiajia.com/ch6/array 0.038s
array_test.go
:
1 | // 数组扩容 |
=== RUN TestArraySliceGrowing
h:\go_learning\src\ch6\array\array_test.go:44: 1 1
h:\go_learning\src\ch6\array\array_test.go:44: 2 2
h:\go_learning\src\ch6\array\array_test.go:44: 3 4
h:\go_learning\src\ch6\array\array_test.go:44: 4 4
h:\go_learning\src\ch6\array\array_test.go:44: 5 8
h:\go_learning\src\ch6\array\array_test.go:44: 6 8
h:\go_learning\src\ch6\array\array_test.go:44: 7 8
h:\go_learning\src\ch6\array\array_test.go:44: 8 8
h:\go_learning\src\ch6\array\array_test.go:44: 9 16
h:\go_learning\src\ch6\array\array_test.go:44: 10 16
— PASS: TestArraySliceGrowing (0.00s)
PASS
ok cs.liangjiajia.com/ch6/array 0.040s
array_test.go
:数组切片共享原数组的存储空间
1 | // 数组切片共享内存空间 |
=== RUN TestSliceShareMemory
h:\go_learning\src\ch6\array\array_test.go:52: [Jun Jul Aug] 3 7
h:\go_learning\src\ch6\array\array_test.go:54: [Jan Feb Mar Apr May Unknow Jul Aug Sep Oct Nov Dec]
— PASS: TestSliceShareMemory (0.00s)
PASS
ok cs.liangjiajia.com/ch6/array 1.499s
Map
⭐src\ch6\map
map_test.go
:
1 | // Map初始化 |
=== RUN TestMapInit
h:\go_learning\src\ch6\map\map_test.go:11: map[apple:1 banana:2 orange:3]
h:\go_learning\src\ch6\map\map_test.go:17: map[orange:3 red:1 yellow:2]
— PASS: TestMapInit (0.00s)
PASS
ok cs.liangjiajia.com/ch6/map 1.487s
键不存在
map_test.go
:
1 | // Map:Key不存在 Value 被初始化为零值,区分 Value 本身是零值 |
=== RUN TestAccessNotExistingKey
h:\go_learning\src\ch6\map\map_test.go:29: Not existing!
— PASS: TestAccessNotExistingKey (0.00s)
PASS
ok cs.liangjiajia.com/ch6/map 0.036s
Map遍历
map_test.go
:
1 | // Map遍历 |
=== RUN TestMapTravel
h:\go_learning\src\ch6\map\map_test.go:40: one 1
h:\go_learning\src\ch6\map\map_test.go:40: two 2
h:\go_learning\src\ch6\map\map_test.go:40: three 3
— PASS: TestMapTravel (0.00s)
PASS
ok cs.liangjiajia.com/ch6/map 0.040s
Map增删改查
map_test.go
:
1 | // Map增删改查 |
=== RUN TestMapOperation
h:\go_learning\src\ch6\map\map_test.go:55: map[four:5 one:1 three:3 two:2] 4
h:\go_learning\src\ch6\map\map_test.go:58: map[four:4 one:1 three:3 two:2] 4
h:\go_learning\src\ch6\map\map_test.go:60: 4
h:\go_learning\src\ch6\map\map_test.go:63: map[one:1 three:3 two:2] 3
— PASS: TestMapOperation (0.00s)
PASS
ok cs.liangjiajia.com/ch6/map 1.517s
字符串
在 语言中,字符串的长度(len
函数返回的值)是按字节数来计算的,而不是按字符数。这意味着一个字符串的长度表示的是它包含的字节数,而不是字符数。所以,不同字符的 编码占用的字节数不同,因此导致字符串的长度不同。
- 一个英文字符等于一个字节
- 一个中文(含繁体)等于三个字节
- 一个中文标点占三个字节
- 一个英文标点占一个字节
⭐src\ch7\string
string_test.go
:
1 | // 字符串对应字节数组长度 |
=== RUN TestString
h:\go_learning\src\ch7\string\string_test.go:7: hello 5
h:\go_learning\src\ch7\string\string_test.go:9: 中 3
— PASS: TestString (0.00s)
PASS
ok cs.liangjiajia.com/ch7/string 1.609s
可以将字符串转换为rune
切片,每个rune
元素表示一个字符的 码点,这允许处理和操作 字符。
string_test.go
:
1 | // 字符串长度 |
=== RUN TestStringToRune
h:\go_learning\src\ch7\string\string_test.go:14: 人民万岁 12
h:\go_learning\src\ch7\string\string_test.go:16: [20154 27665 19975 23681] 4
h:\go_learning\src\ch7\string\string_test.go:18: 人 4eba
h:\go_learning\src\ch7\string\string_test.go:18: 民 6c11
h:\go_learning\src\ch7\string\string_test.go:18: 万 4e07
h:\go_learning\src\ch7\string\string_test.go:18: 岁 5c81
— PASS: TestStringToRune (0.00s)
PASS
ok cs.liangjiajia.com/ch7/string (cached)
string_test.go
:
1 | // 字符串相关函数 |
=== RUN TestStringFn
h:\go_learning\src\ch7\string\string_test.go:33: A
h:\go_learning\src\ch7\string\string_test.go:33: B
h:\go_learning\src\ch7\string\string_test.go:33: C
h:\go_learning\src\ch7\string\string_test.go:35: A-B-C
— PASS: TestStringFn (0.00s)
PASS
ok cs.liangjiajia.com/ch7/string 1.493s
string_test.go
:
1 | // String与数字相互转换 |
=== RUN TestStringConvDigital
h:\go_learning\src\ch7\string\string_test.go:41: 1010
h:\go_learning\src\ch7\string\string_test.go:44: 20
— PASS: TestStringConvDigital (0.00s)
PASS
ok cs.liangjiajia.com/ch7/string 0.040s
函数
⭐src\ch8\function
function_test.go
:计算一个函数所花的时间(使用函数作为参数和返回值)。
1 | func timeSpent(inner func()) func() { |
=== RUN TestFunction
time spent: 1.0077673
— PASS: TestFunction (1.01s)
PASS
ok cs.liangjiajia.com/ch8/function 6.640s
function_test.go
:计算任意多个值得和(函数可变参数)。
1 | func Sum(ops ...int) int { |
=== RUN TestFunctionVarParam
h:\go_learning\src\ch8\function\function_test.go:35: 10
h:\go_learning\src\ch8\function\function_test.go:36: 15
— PASS: TestFunctionVarParam (0.00s)
PASS
ok cs.liangjiajia.com/ch8/function 5.636s
function_test.go
:抛出异常,释放资源( 函数)。
1 | func TestFunctionDefer(t *testing.T) { |
=== RUN TestFunctionDefer
h:\go_learning\src\ch8\function\function_test.go:43: Started
h:\go_learning\src\ch8\function\function_test.go:41: Clear resources
— FAIL: TestFunctionDefer (0.00s)
panic: Fatal error [recovered]
panic: Fatal error
面向对象编程
类和对象
1 | type struct_variable_type struct { |
⭐src\ch9\object
属性定义:
encapsulation\encapsulation_test.go
1 | type Employee struct { |
1 | func TestCreateEmployeeObj(t *testing.T) { |
=== RUN TestCreateEmployeeObj
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:17: {0 Bob 20}
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:20: { Mike 30}
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:26: &{2 Rose 22}
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:28: e1 is encapsulation_test.Employee
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:29: e2 is encapsulation_test.Employee
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:30: e3 is *encapsulation_test.Employee
— PASS: TestCreateEmployeeObj (0.00s)
PASS
ok cs.liangjiajia.com/ch9/object/encapsulation 0.038s
行为定义:
encapsulation\encapsulation_test.go
1 | func (e *Employee) String1() string { |
=== RUN TestStructOperations
h:\go_learning\src\ch9\object\encap_test.go:42: ID:0/Name:Bob/Age:20
h:\go_learning\src\ch9\object\encap_test.go:43: ID:0-Name:Bob-Age:20
h:\go_learning\src\ch9\object\encap_test.go:46: ID:1/Name:Alice/Age:20
h:\go_learning\src\ch9\object\encap_test.go:47: ID:1-Name:Alice-Age:20
— PASS: TestStructOperations (0.00s)
PASS
ok cs.liangjiajia.com/ch9/object 1.430s
通过指针和实例来表述是一样的。
encapsulation\encapsulation_test.go
:通过地址打印查看区别
1 | func (e *Employee) String1() string { |
=== RUN TestStructOperations
Address is c000074910Address is c000074910
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:47: ID:0/Name:Bob/Age:20
Address is c000074970
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:48: ID:0-Name:Bob-Age:20Address is c0000749a0
Address is c0000749a0
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:55: ID:1/Name:Alice/Age:20
Address is c0000749d0
h:\go_learning\src\ch9\object\encapsulation\encapsulation_test.go:56: ID:1-Name:Alice-Age:20
— PASS: TestStructOperations (0.00s)
PASS
ok cs.liangjiajia.com/ch9/object/encapsulation 1.514s
接口
1 | type interface_name interface { |
⭐src\ch9\object
interface\interface_test.go
:
1 | type Programmer interface { |
=== RUN TestClient
h:\go_learning\src\ch9\object\interface\interface_test.go:18: fmt.Println(“Hello World”)
— PASS: TestClient (0.00s)
PASS
ok cs.liangjiajia.com/ch9/object/interface 0.039s
空接口
interface\empty_interface_test.go
:
1 | func DoSomething(p interface{}) { |
=== RUN TestEmptyInterfaceAssertion
Integer 10
String 10
— PASS: TestEmptyInterfaceAssertion (0.00s)
PASS
ok cs.liangjiajia.com/ch9/object/interface 1.447s
自定义类型
⭐src\ch9\object
customer_type_test.go
:
1 | package object_test |
=== RUN TestFunction
time spent: 1.0059892
— PASS: TestFunction (1.01s)
PASS
ok cs.liangjiajia.com/ch9/object (cached)
拓展复用
⭐src\ch9\object
extension\extension_test.go
:宠物类
1 | type Pet struct { |
extension\extension_test.go
:狗类
1 | type Dog struct { |
上述也可以简单表示:extension\extension_test.go
1 | type Dog struct { |
1 | func TestDog(t *testing.T) { |
=== RUN TestDog
… solisamicus
— PASS: TestDog (0.00s)
PASS
ok cs.liangjiajia.com/ch9/object 5.659s
做部分修改:extension\extension_test.go
1 | type Dog struct { |
=== RUN TestDog
Woof Woof Woof solisamicus
— PASS: TestDog (0.00s)
PASS
ok cs.liangjiajia.com/ch9/object/extension 5.801s
多态
⭐src\ch9\object
polymorphism\polymorphism_test.go
:
1 | type Programmer interface { |
=== RUN TestClient
*polymorphism_test.GoProgrammer fmt.Println(“Hello World”)
*polymorphism_test.JavaProgrammer System.out.Println(“Hello World!”)
— PASS: TestClient (0.00s)
PASS
ok cs.liangjiajia.com/ch9/object/polymorphism 1.522s
错误处理
⭐src\ch10\error
error_test.go
:对于 数列要求
1 | var LessThanTwoError = errors.New("n should be not less than 2") |
=== RUN TestGetFibonacci
h:\go_learning\src\ch10\error\error_test.go:29: n should be not less than 2
— FAIL: TestGetFibonacci (0.00s)
FAIL
FAIL cs.liangjiajia.com/ch10/error 2.071s
其他两种改写方式:
1⃣error_test.go
:
1 | func GetFibonacci1(str string) { |
=== RUN TestGetFibonacc1
Error n should be not less than 2
[1 1 2 3 5 8 13 21 34 55]
— PASS: TestGetFibonacc1 (0.00s)
PASS
ok cs.liangjiajia.com/ch10/error 0.045s
2⃣error_test.go
:
1 | func GetFibonacci2(str string) { |
=== RUN TestGetFibonacc2
Error n should be not less than 2
[1 1 2 3 5 8 13 21 34 55]
— PASS: TestGetFibonacc2 (0.00s)
PASS
ok cs.liangjiajia.com/ch10/error 1.426s
包和依赖管理
语言中的标识符(变量、函数、类型等)的可见性是由首字母的大小写来决定的。
- 大写字母开头的标识符是公开的,可以被其他包访问;
- 小写字母开头的标识符是私有的,只能在当前包内使用。
语言的编译和导入机制并不直接要求包名和目录名一致,但强烈建议保持它们一致,以提高代码的可读性和维护性。
同一目录的 的 要保持一致。
当建立工作空间目录后,需要把工作空间目录的路径添加的 环境变量中。
⭐src\ch11\series
my_series.go
:
1 | package series |
⭐src\ch11\client
package_test.go
:
1 | package client |
=== RUN TestPackage
h:\go_learning\src\ch11\client\package_test.go:10: [1 1 2 3 5]
— PASS: TestPackage (0.00s)
PASS
ok cs.liangjiajia.com/ch11/client 1.539s
使用第三方库
go get -u github.com/easierway/concurrent_map
remote_package_test.go
:
1 | package client |
=== RUN TestConcurrentMap
h:\go_learning\src\ch11\client\remote_package_test.go:12: 10 true
— PASS: TestConcurrentMap (0.00s)
PASS
ok cs.liangjiajia.com/ch11/client 1.148s
并发编程
协程机制
⭐src\ch12\groutine
groutine_test.go
:
1 | func TestGroutine(t *testing.T) { |
=== RUN TestGroutine
0
3
2
5
4
6
7
8
9
1
— PASS: TestGroutine (0.06s)
PASS
ok cs.liangjiajia.com/ch12/groutine (cached)
共享内存机制
⭐src\ch12\share_memery
share_memery.go
:
1 | func TestCounter(t *testing.T) { |
=== RUN TestCounter
h:\go_learning\src\ch12\share_memory\share_memery_test.go:17: counter = 4942
— PASS: TestCounter (1.02s)
PASS
ok cs.liangjiajia.com/ch12/share_memory 6.647s
在这个例子中,每个 都试图递增一个共享的counter
变量。由于 并发执行,它们可能会同时访问和修改counter
变量,导致竞争条件()。不同的 可能在不同的时间点并发地执行counter++
操作。最终的counter
值就不再是预期的 。
share_memery.go
:解决方法,使用互斥锁()来保护counter
变量,确保同一时间只有一个 可以访问。
1 | func TestCounterThreadSafe(t *testing.T) { |
=== RUN TestCounterThreadSafe
h:\go_learning\src\ch12\share_memory\share_memery_test.go:33: counter = 5000
— PASS: TestCounterThreadSafe (1.01s)
PASS
ok cs.liangjiajia.com/ch12/share_memory 1.054s
share_memery.go
:time.Sleep(1 * time.Second)
被用来等待足够的时间,以确保所有启动的 都有机会执行递增操作,从而在最终打印计数器值之前完成。在这个例子中, 是并发执行的,执行时间是不确定的,因此在主 (测试函数)继续执行之前,使用Sleep
来等待一秒钟,以确保足够的时间让其他 完成它们的递增操作。需要注意的是,使用Sleep
并不是一种理想的等待方式,因为它不能保证所有的 都已经完成。更好的做法是使用等待组()等并发控制机制,这样可以更精确地等待所有 完成后再继续执行。
1 | func TestCounterWaitGroup(t *testing.T) { |
=== RUN TestCounterWaitGroup
h:\go_learning\src\ch12\share_memory\share_memery_test.go:52: counter = 5000
— PASS: TestCounterWaitGroup (0.00s)
PASS
ok cs.liangjiajia.com/ch12/share_memory 5.661s
CSP并发机制
语言通过 () 并发模型来实现并发编程。 是一种并发编程的范式,强调通过在不同执行体 之间传递消息进行通信,而不是通过共享内存来进行数据共享。
核心组件:
(协程): 语言中轻量级的并发执行单元,可以看作是一个独立的执行线程。使用go
关键字可以启动一个新的 ,它和主 并发执行。由 运行时系统调度,而不是由操作系统调度,创建和切换成本相对较低。
(通道):用于在不同goroutines之间传递数据的管道。通道可以是带缓冲的(缓冲通道)或非缓冲的。带缓冲通道允许在发送和接收之间有一定的缓冲空间,而非缓冲通道要求发送和接收同时准备好。通过使用通道, 可以安全地进行数据传递,避免了显式的锁和共享内存的复杂性。
⭐src\ch12\async_service
async_service_test.go
:串行输出
1 | func service() string { |
=== RUN TestService
Done
working on something else
Task is done.
— PASS: TestService (0.17s)
PASS
ok cs.liangjiajia.com/ch12/async_service 5.886s
async_service_test.go
:并行控制
1 | func AsyncService() chan string { |
=== RUN TestAsynService
working on something else
returned result.
Task is done.
Done
service exited.
— PASS: TestAsynService (0.10s)
PASS
ok cs.liangjiajia.com/ch12/async_service 5.777s
执行过程:
-
TestAsyncService
开始执行,启动主 ; -
AsyncService
被调用,启动一个 。在这个 中,service
被调用 ,休眠 ms; -
otherTask
被调用,启动一个 。在这个 中,打印"",休眠 ms; -
毫秒后,
service
完成,打印"",将 “” 通过通道传递给主 ;
另外的 毫秒后,otherTask
完成,打印""。
-
主 从通道中接收到"",打印"";
-
AsyncService
中的 继续执行,打印 “”。
service
中 会发生堵塞,直到接收 。
async_service_test.go
:稍作修改
1 | func AsyncService() chan string { |
=== RUN TestAsynService
working on something else
returned result.
service exited.
Task is done.
Done
— PASS: TestAsynService (0.11s)
PASS
ok cs.liangjiajia.com/ch12/async_service (cached)
多路选择和超时控制
⭐src\ch12\select_case
select_case_test.go
:
1 | func AsyncService() chan string { |
select_case_test.go
:设置service
等待时间为 ms
1 | func service() string { |
=== RUN TestSelect
returned result.
service exited.
h:\go_learning\src\ch12\select_case\select_case_test.go:28: Done
— PASS: TestSelect (0.06s)
PASS
ok cs.liangjiajia.com/ch12/select_case 5.687s
select_case_test.go
:设置service
等待时间为 ms
1 | func service() string { |
=== RUN TestSelect
h:\go_learning\src\ch12\select_case\select_case_test.go:30: time out
— FAIL: TestSelect (0.10s)
FAIL
FAIL cs.liangjiajia.com/ch12/select_case 5.778s
Channel的关闭和消息广播
⭐src\ch12\channel
channel_close_test.go
:单个接收者
1 | func dataProducer(ch chan int, wg *sync.WaitGroup) { |
=== RUN TestCloseChannel
0
1
2
3
4
5
6
7
8
9
— PASS: TestCloseChannel (0.00s)
PASS
ok cs.liangjiajia.com/ch12/channel 1.640s
channel_close_test.go
:多个接收者
1 | func dataProducer(ch chan int, wg *sync.WaitGroup) { |
=== RUN TestCloseChannel
0
2
3
4
1
6
7
8
9
5
— PASS: TestCloseChannel (0.00s)
PASS
ok cs.liangjiajia.com/ch12/channel 1.440s
普通任务取消
⭐src\ch12\cancel
cancel_test.go
:
1 | package cancel |
1⃣cancel_test.go
:发送空消息
1 | // cancel_1 sends a cancellation signal to the provided channel. |
1 | func TestCancel(t *testing.T) { |
=== RUN TestCancel
4 Cancelled
— PASS: TestCancel (1.00s)
PASS
ok cs.liangjiajia.com/ch12/cancel 6.779s
2⃣cancel_test.go
:关闭通道
1 | // cancel_2 sends a cancellation signal to the provided channel. |
1 | func TestCancel(t *testing.T) { |
=== RUN TestCancel
4 Cancelled
0 Cancelled
1 Cancelled
2 Cancelled
3 Cancelled
— PASS: TestCancel (1.01s)
PASS
ok cs.liangjiajia.com/ch12/cancel 6.547s
关联任务取消
⭐src\ch12\cancel
cancel_by_context_test.go
:
1 | func isCancelledContext(ctx context.Context) bool { |
典型并发任务
⭐src\ch12\concurrent_tasks
单例模式
singleton\singleton_test.go
:
1 | type Singleton struct { |
=== RUN TestGetSingletonObj
Create Obj
C00008A000
C00008A000
C00008A000
C00008A000
C00008A000
C00008A000
C00008A000
C00008A000
C00008A000
C00008A000
— PASS: TestGetSingletonObj (0.00s)
PASS
ok cs.liangjiajia.com/ch12/concurrent_tasks/singleton 1.778s
返回第一个
first_response\first_response_test.go
:多个任务的并发执行,但只等待第一个返回的结果。
1 | func runTask(id int) string { |
=== RUN TestFirstResponse
h:\go_learning\src\ch12\concurrent_tasks\first_response\first_response_test.go:27: The result is from 9
— PASS: TestFirstResponse (0.02s)
PASS
ok cs.liangjiajia.com/ch12/concurrent_tasks/first_response 5.685s
完成所有任务
all_response\all_response_test.go
:多个任务的并发执行,等待所有返回的结果。
1 | func runTask(id int) string { |
=== RUN TestFirstResponse
h:\go_learning\src\ch12\concurrent_tasks\all_response\all_response_test.go:32:
The result is from 1
The result is from 6
The result is from 9
The result is from 7
The result is from 0
The result is from 4
The result is from 5
The result is from 3
The result is from 2
The result is from 8
— PASS: TestFirstResponse (1.02s)
PASS
ok cs.liangjiajia.com/ch12/concurrent_tasks/all_response 6.766s
对象池
⭐src\ch12\object_pool
obj_pool.go
:
1 | type ReusableObj struct { |
obj_pool_test.go
:
1 | func TestObjPool(t *testing.T) { |
=== RUN TestObjPool
0xc00000a068
0xc00000a070
0xc00000a078
0xc00000a080
0xc00000a088
0xc00000a090
0xc00000a098
0xc00000a0a0
0xc00000a0a8
0xc00000a0b0
0xc00000a0b8
0xc00000a0c0
0xc00000a0c8
0xc00000a0d0
0xc00000a0d8
Done
— PASS: TestObjPool (0.00s)
PASS
ok cs.liangjiajia.com/ch12/object_pool 1.435s
sysc.Pool 对象缓存
对象获取:
- 尝试从私有对象获取
- 尝试从当前 共享池获取
- 尝试从其他 共享池获取
- 用户指定 函数产生新的对象
对象放回:
- 保存为私有对象
- 保存为当前 共享池
⭐src\ch12\object_cache
sysnc_pool_test.go
:
1 | func TestSyncPool(t *testing.T) { |
=== RUN TestSyncPool
Create a new object.
100
2023
— PASS: TestSyncPool (0.00s)
PASS
ok cs.liangjiajia.com/ch12/object_cache (cached)
sysnc_pool_test.go
:添加runtime.GC()
:官方 将对sync.Pool中的对象回收时机策略做出调整。在 版本及以前的版本中,在每轮垃圾回收过程中,每个 实例中的所有缓存对象都将被无条件回收掉。
1 | func TestSyncPool(t *testing.T) { |
=== RUN TestSyncPool
Create a new object.
100
2023
— PASS: TestSyncPool (0.00s)
PASS
ok cs.liangjiajia.com/ch12/object_cache (cached)
sysnc_pool_test.go
:从 版本开始,如果一个 实例在上一轮垃圾回收过程结束之后仍然被使用过,则其中的缓存对象将不会被回收掉。此举对于使用 来提升效率的程序来说,将大大减少周期性的因为缓存被清除而造成的瞬时效率下降。
1 | func TestSyncPool(t *testing.T) { |
=== RUN TestSyncPool
Create a new object.
100
Create a new object.
100
— PASS: TestSyncPool (0.00s)
PASS
ok cs.liangjiajia.com/ch12/object_cache (cached)
sysnc_pool_test.go
:
1 | func TestSyncPoolInMultiGroutine(t *testing.T) { |
=== RUN TestSyncPoolInMultiGroutine
100
100
100
Create a new object.
10
Create a new object.
10
Create a new object.
10
Create a new object.
10
Create a new object.
10
Create a new object.
10
Create a new object.
10
— PASS: TestSyncPoolInMultiGroutine (0.00s)
PASS
ok cs.liangjiajia.com/ch12/object_cache 0.037s
测试
单元测试
⭐src\ch13\unit_test
square.go
:
1 | func square(op int) int { |
square_test.go
:
1 | func TestSquare(t *testing.T) { |
=== RUN TestSquare
— PASS: TestSquare (0.00s)
PASS
ok cs.liangjiajia.com/ch13/unit_test 1.270s
Fail
,Error
:测试失败,测试继续,其他测试继续执行。
FailNow
,Fatal
:测试失败,测试终止,其他测试继续执行。
square_test.go
:
1 | func TestErrorInCode(t *testing.T) { |
=== RUN TestSquare
— PASS: TestSquare (0.00s)
=== RUN TestErrorInCode
Start
h:\go_learning\src\ch13\unit_test\square_test.go:21: Error
End
— FAIL: TestErrorInCode (0.00s)
=== RUN TestFailInCode
Start
h:\go_learning\src\ch13\unit_test\square_test.go:27: Error
— FAIL: TestFailInCode (0.00s)
FAIL
FAIL cs.liangjiajia.com/ch13/unit_test 1.415s
断言
go get -u github.com/stretchr/testify/assert
1 | func TestSquareWithAssert(t *testing.T) { |
=== RUN TestSquareWithAssert
— PASS: TestSquareWithAssert (0.00s)
PASS
ok cs.liangjiajia.com/ch13/unit_test 0.940s
发生错误的情况
=== RUN TestSquareWithAssert
h:\go_learning\src\ch13\unit_test\square_test.go:38:
Error Trace: h:/go_learning/src/ch13/unit_test/square_test.go:38
Error: Not equal:
expected: 10
actual : 9
Test: TestSquareWithAssert
— FAIL: TestSquareWithAssert (0.00s)
FAIL
FAIL cs.liangjiajia.com/ch13/unit_test 1.819s
Benchmark
⭐src\ch13\benchmark
concat_test.go
:
1 | func TestConcatStringByAdd(t *testing.T) { |
go test -bench="."
goos: windows
goarch: amd64
pkg: cs.liangjiajia.com/ch13/benchmark
cpu: Intel® Core™ i7-9750H CPU @ 2.60GHz
BenchmarkConcatStringByAdd-12 11036227 105.3 ns/op
BenchmarkConcatStringByBytesBuffer-12 21769615 54.71 ns/op
PASS
ok cs.liangjiajia.com/ch13/benchmark 4.115s
go test -bench="." -benchmem
goos: windows
goarch: amd64
pkg: cs.liangjiajia.com/ch13/benchmark
cpu: Intel® Core™ i7-9750H CPU @ 2.60GHz
BenchmarkConcatStringByAdd-12 11269498 108.7 ns/op 16 B/op 4 allocs/op
BenchmarkConcatStringByBytesBuffer-12 19745870 56.24 ns/op 64 B/op 1 allocs/op
PASS
ok cs.liangjiajia.com/ch13/benchmark 2.554s
BDD-Behavior Driven Development
go get -u github.com/smartystreets/goconvey/convey
bdd_test.go
:
1 | func TestSpec(t *testing.T) { |
反射
reflect.TypeOf
返回类型reflect.Type
reflect.ValueOf
返回值reflect.Value
判断类型:
1 | type Kind uint |
⭐src\ch14\reflect
reflect_test.go
:
1 | func TestTypeAndValue(t *testing.T) { |
=== RUN TestTypeAndValue
h:\go_learning\src\ch14\reflect\reflect_test.go:11: int64 10
h:\go_learning\src\ch14\reflect\reflect_test.go:12: int64
— PASS: TestTypeAndValue (0.00s)
PASS
ok cs.liangjiajia.com/ch14/reflect 0.040s
reflect_test.go
:
1 | func CheckType(v interface{}) { |
=== RUN TestBasicType
Float
— PASS: TestBasicType (0.00s)
PASS
ok cs.liangjiajia.com/ch14/reflect 1.529s
利用反射编写灵活的代码:
- 按名字访问结构的成员:
reflect.ValueOf(*e).FieldByName("XXX")
- 按名字访问结构的方法:
reflect.ValueOf(e).MethodByName("XXX").Call([]reflect.Value{reflect.ValueOf(x)})
reflect_test.go
:
1 | type Employee struct { |
=== RUN TestInvokeByName
h:\go_learning\src\ch14\reflect\reflect_test.go:44: Name: value(Mike), Type(reflect.Value)
h:\go_learning\src\ch14\reflect\reflect_test.go:48: Tag:format normal
h:\go_learning\src\ch14\reflect\reflect_test.go:51: Updated Age: &{1 Mike 18}
— PASS: TestInvokeByName (0.00s)
PASS
ok cs.liangjiajia.com/ch14/reflect (cached)
万能程序
reflect_deepequal_test.go
:
1 | type Customer struct { |
=== RUN TestDeepEqual
h:\go_learning\src\ch14\reflect\reflect_deepequal_test.go:17: a==b? true
h:\go_learning\src\ch14\reflect\reflect_deepequal_test.go:22: s1 == s2? true
h:\go_learning\src\ch14\reflect\reflect_deepequal_test.go:23: s1 == s3? false
h:\go_learning\src\ch14\reflect\reflect_deepequal_test.go:27: c1 == c2? true
— PASS: TestDeepEqual (0.00s)
PASS
ok cs.liangjiajia.com/ch14/reflect 1.636s
reflect_deepequal_test.go
:我们可以观察到Employee
类和Customer
类结构一致,所以可以在值一样的情况下完成填充
1 | func fillBySettings(st interface{}, settings map[string]interface{}) error { |
1 | func TestFillNameAndAge(t *testing.T) { |
=== RUN TestFillNameAndAge
h:\go_learning\src\ch14\reflect\reflect_deepequal_test.go:66: { Mike 30}
h:\go_learning\src\ch14\reflect\reflect_deepequal_test.go:71: { Mike 30}
— PASS: TestFillNameAndAge (0.00s)
PASS
ok cs.liangjiajia.com/ch14/reflect 1.825s
常见架构模式实现
pipe-filter
filter.go
定义:
1 | // Request is the input of the filter |
split_filter.go
:
1 | var SplitFilterWrongFormatError = errors.New("Input data should be string") |
to_int_filter.go
:
1 | var ToIntFilterWrongFormatError = errors.New("Input data should be []string") |
sum_filter.go
:
1 | var SumFilterWrongFormatError = errors.New("Input data should be []int") |
straigt_pipeline.go
:整合形成
1 | // NewStraightPipeline create a new StraightPipelineWithWallTime |
pipefilter_test.go
:
1 | func TestStraightPipeline(t *testing.T) { |
=== RUN TestStraightPipeline
— PASS: TestStraightPipeline (0.00s)
PASS
ok cs.liangjiajia.com/ch15/framework/pipe_filter 1.404s
micro-kernel
常见任务
JSON
⭐src\ch16\task\json
struct_def.go
:
1 | type BasicInfo struct { |
embedded_json_test.go
:
1 | func TestEmbeddedJson(t *testing.T) { |
=== RUN TestEmbeddedJson
{ {Mike 30} {[Java Go C]} }
{“basic_info”:{“name”:“Mike”,“age”:30},“job_info”:{“skills”:[“Java”,“Go”,“C”]}}
— PASS: TestEmbeddedJson (0.00s)
PASS
ok cs.liangjiajia.com/ch16/task/json 1.437s
easyjson
至 src\ch16,执行
go get -u github.com/mailru/easyjson & go install github.com/mailru/easyjson/...@latest
至 src\ch16\task\json,执行
~\go\bin\easyjson -all struct_def.go
自动生成struct_def_easyjson.go
文件
easy_json_test.go
:
1 | func TestEasyJson(t *testing.T) { |
=== RUN TestEasyJson
{ {Mike 30} {[Java Go C]} }
{“basic_info”:{“name”:“Mike”,“age”:30},“job_info”:{“skills”:[“Java”,“Go”,“C”]}}
— PASS: TestEasyJson (0.00s)
PASS
ok cs.liangjiajia.com/ch16/task/json 1.662s
benchmark_json_test.go
:
1 | func BenchmarkEmbeddedJson(b *testing.B) { |
至 src\ch16\task\json,执行
go test -bench="."
{ {Mike 30} {[Java Go C]} }
{“basic_info”:{“name”:“Mike”,“age”:30},“job_info”:{“skills”:[“Java”,“Go”,“C”]}}
{ {Mike 30} {[Java Go C]} }
{“basic_info”:{“name”:“Mike”,“age”:30},“job_info”:{“skills”:[“Java”,“Go”,“C”]}}
goos: windows
goarch: amd64
pkg: cs.liangjiajia.com/ch16/task/json
cpu: Intel® Core™ i7-9750H CPU @ 2.60GHz
BenchmarkEmbeddedJson-12 530538 2283 ns/op
BenchmarkEasyJson-12 1568922 717.6 ns/op
PASS
ok cs.liangjiajia.com/ch16/task/json 3.182s
HTTP
⭐src\ch16\task\http
hello_http.go
:
1 | package main |
restful
至 src\ch16,执行
go get -u github.com/julienschmidt/httprouter
http_router\http_router_http.go
:
1 | package main |
http://127.0.0.1:8080/hello/:solisamicus
restful\restful_http.go
:
1 | type Employee struct { |
http://127.0.0.1:8080/employee/Mike
性能调优
graphviz
下载:https://graphviz.org/download/
flamegraph
下载并复制 (https://github.com/brendangregg/FlameGraph/blob/master/flamegraph.pl)至 \text {\$GOPATH\bin}
go-torch
是 公司开源的一款针对 程序的火焰图生成工具,能收集 ,并把它们整理成火焰图,直观地呈现给开发人员。
至 src\ch17,执行
go get -u github.com/uber/go-torch && go install github.com/uber/go-torch@latest
⭐src\ch17\tools
prof.go
:
1 | package main |
- 至 src\ch17\tools,执行
go build prof.go
- 生成可执行文件 ,执行
.\prof.exe
- 生成相关的 文件:,,,执行
go tool pprof prof cpu.prof
依次输入top
,list XXX
查看详细情况:
分析:
top
:展示了运行时间最长的 个函数,以及运行时间占总运行时间的百分比和累积时间。
指标 | 说明 |
---|---|
函数自身 运行时间 | |
\text {flat%} | 函数自身运行时间占整个程序 运行时间中所占的百分比 |
\text {sum%} | 函数自身运行时间以及它调用的其他函数的运行时间占整个程序 运行时间的累积百分比 |
函数自身运行时间以及它调用的其他函数的 运行时间的累积时间 | |
\text {cum%} | 函数自身运行时间以及它调用的其他函数的运行时间占整个程序 运行时间的累积百分比 |
list fillMatrix
指标 | 说明 |
---|---|
函数自身 运行时间 | |
函数自身运行时间以及它调用的其他函数的 运行时间的累积时间 | |
$\text |