-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgolang 基础
1001 lines (895 loc) · 22.4 KB
/
golang 基础
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1.变量
声明:标准格式 var 变量名 变量类型
批量格式:var (
a int
b string
c []string
d func() bool
e struct {
x int
}
)
1.1初始化
var 变量名 变量类型 = 表达式
var hp int = 9
var hp = 9 编译器会推导变量的的类型
hp := 9
var a int = 100
var a , b , c = 1,2,3
1.2 数据类型
整型,浮点型,布尔型,字符串 字符 切片 map 结构体 函数 通道
整型:int8 int16 int32 int64
uint8 uint16 uint32 uint64
浮点型:float32 float64
布尔型:true false
字符串:var a = "abx"
字符:byte uint8
切片:[]byte by := make([]byte , 10)
1.3 指针
a := 9
b := &a
*b = 15
fmt.Println(a , *b , b)
//15 15 0xc00005c068
*a 指针类型
func (a *int)int{
return *a
}
1.4 创建指针的另外一种办法 new
s := new(string)
fmt.Println(s)
*s = "123"
fmt.Println(s , *s)
//0xc0000321f0
//0xc0000321f0 123
1.5 变量的生命周期
栈:是一种拥有特殊规则的线性表数据结构
栈只允许往线性表的一端放入数据,之后从另一端取出数据 按照后进先出的顺序
理解:栈的原理类似于将书一本本的堆起来 书按顺序一本本的从顶部放入 要去书市只能从顶部一本本的取出
堆:就像房子里面摆家具会形成内存碎片
go自动识别变量是分配到堆上 还是分配到栈上
1.6 字符串应用
len(string) 获取字符串的长度
遍历ascll码字符串直接使用下标
Unicode字符串遍历使用for range
s := "encode to string !!!"
bs := base64.StdEncoding.EncodeToString([]byte(s))
s1 , err := base64.StdEncoding.DecodeString(bs)
fmt.Println(s , bs , string(s1) , err)
//encode to string !!! ZW5jb2RlIHRvIHN0cmluZyAhISE= encode to string !!! <nil>
读取文件
file , err := os.Open(filename)
if err != nil {
return
}
defer file.close
fmt.Println(file)
reader := bufio.NewReader(file)
for{
linestr , err := reader.ReadString('\n')
if err != nil {
break
}
}
1.7 常量
相对于变量,常量是很定不变的值
枚举
const (
a = iota
b
c
d
)
1.8 类型别名
1.9之前 type byte uint8
type rune int32
1.9之后 type byte = uint8
type rune = int32
类型定义与区分类型别名
type typeAlias = type //别名
type NewInt int //定义类型
type IntAlias = int //定义int的别名
var a NewInt
var b IntAlias
fmt.Printf("a type : %T\n b type : %T\n" , a , b)
//a type : NewInt
//b type int
2.1 数组-固定大小的连续空间
2.1.1 声明数据
var 数组变量名 [元素个数]元素类型
var ints [3]int
2.1.2 初始化数组
var ints = [3]int{1,2,3}
var ints = [...]int{1}//定义不固定数量元素的数组
2.1.3 遍历数组
for k , v := range ints {
fmt.Println( k , v)
}
2.2.1 切片 -- 动态分配大小的连续空间
2.2.1从数组或切片生成新的切片
slice[开始位置:结束位置]
var s [3]string//定义变量 但是未初始化
var s1 = [3]string{"1" , "2" , "3"}//定义数组变量并初始化
//生成新的切片
s2 := s1[1:2]
s3 := s1[1:3]
fmt.Println(s , s1 , s2 , s3)
//[ ] [1 2 3] [2] [2 3]
s4 := s1[:]//表示原有切片
s5 := s1[0:0]//重置切片
2.2.2 切片的声明
var name []T
//声明字符串切片
var s1 []string
//声明整型切片
var s2 []int
var s1 []string
fmt.Println(s1)
s1 = append(s1 , "aaa")
fmt.Println(s1)
//[]
//[aaa]
2.2.3 使用make()函数构造切片
格式:make([]int , size , cap)
T 切片的元素类型
size 为这个类型分配多少个元素
cap 预分配元素数量 这个值的设定不影响size 只是提前分配空间 降低多次分配空间造成的性能问题
a := make([]int , 10)
b := make([]int , 10 , 1024)
2.2.4 使用append函数为切片添加元素
s1 := make([]int , 10)
s2 := make([]int , 5)
s1 = append(s1 , 12)
fmt.Println(s1)
s1 = append(s1 , s2...)
fmt.Println(s1)
//[0 0 0 0 0 0 0 0 0 0 12]
//[0 0 0 0 0 0 0 0 0 0 12 0 0 0 0 0]
2.2.5 切片的复制
copy(destSlice , srcSlice[]T)int
2.2.6 切片元素的删除
s1 := []int{1,2,3,4,5,6,7,8,9}
//删除3
fmt.Println(append(s1[0:2] , s1[3:]...))
//[1 2 4 5 6 7 8 9]
2.3映射 map
map使用散列hash实现
2.3.1 map的添加与访问数据
map[T]T
var m map[int]int//定义m变量 但是未实例化 不可使用//panic: assignment to entry in nil map
var m = map[int]int{1:1} //定义变量并且初始化
m[1] = 2
fmt.Println(m)
//map[1:2]
var m = make(map[int]int , 10)
m[1] = 21
fmt.Println(m)
//map[1:21]
2.3.2 遍历map键值对
for k , v := range m {
fmt.Println(k , v)
}
2.3.3 使用delete函数 删除map键值对
m := delete(m , 1)
fmt.Println(m)
//map[]
2.3.4 清空map
m = make(map[] , 0)//新建一个map 赋值给m 清空了原来的数据
2.3.5 并发环境中使用map -- sync.map
var m sync.Map
m.Store(1 , 21)
m.Store(2 , 21)
m.Store(3 , 21)
fmt.Println(m)
m.Delete(1)
fmt.Println(m)
fmt.Println(m.Load(2))
fmt.Println(m.LoadOrStore(5 , 43))
m.Range(func(key, value interface{}) bool {
fmt.Println(key , value)
return true
})
//{{0 0} {{map[] true}} map[1:0xc00008a018 2:0xc00008a020 3:0xc00008a028] 0}
//{{0 0} {{map[] true}} map[2:0xc00008a020 3:0xc00008a028] 0}
//21 true
//43 false
//5 43
//2 21
//3 21
2.4 列表 list -- 可以快速增加删除的非连续空间的容器
列表有多种实现方法,单链表 双链表
2.4.1 初始化列表
变量名:= list.New()
var 变量名 list.List
2.4.2 在列表中插入元素
l := list.List{}
l.PushBack("123")//尾部添加
l.PushFront("345")
l.PushFront("456")
l.PushBack("456")
el := l.PushBack("345")
for i := l.Front() ; i != nil;i = i.Next() {
fmt.Println(i.Value)
}
l.Remove(el)
fmt.Println("remove")
for i := l.Front() ; i != nil;i = i.Next() {
fmt.Println(i.Value)
}
fmt.Println(l , l.Len())
3 流程控制
3.1 条件判断(if)
if 表达式 {
}else if 表达式2{
}else {
}
特殊写法
if err := Connect();err != nil {
fmt.println(err)
return
}
3.2 循环 for
for 出事语句;条件表达式;结束语句 {
循环体代码
}
step := 2
for ; ; step ++ {
if step < 10 {
break
}
}
无限循环代码
for {
循环体代码
}
九九乘法表代码:
for i := 1 ; i <= 9 ; i++ {
for j := 1 ; j <= 9 ; j ++ {
if i < j {
continue
}
fmt.Print(j ,"*" , i , "=" , i*j , " ")
}
fmt.Println()
}
键值循环 for range
//chan
ch := make([]chan , 10)
for k , v := range ch {
fmt.Println(k , v)
}
//map
var m = make(map[string]string , 10)
m["12"] = "123"
for k , v := range m {
fmt.Println("aaa" , k , v)
}
//string
var s = "123456"
for k , v := range s {
fmt.Println(" k => v " , k , v)
}
//数组
var a = [3]int{1,2,3}
for k , v := range a {
fmt.Println(k , v)
}
//切片
s := [...]int{1,2,3,4,5,6}
for k , v := range s {
fmt.Println(k , v)
}
3.3 分支 switch
case 不需要break 如果case 1执行成功 需要继续下一个case执行可以 fallthrough
var a = 1
switch a {
case 1:
fmt.Println(1)
case 2:
fmt.Println(2)
default:
fmt.Println(0)
}
一分支可多值:
case 1 , 2:
分支表达式:
case r > 10 && r < 100:
var a = 1
switch {
case a == 1:
fmt.Println("1")
fallthrough//执行紧接着下一个case 会打印出 ==2
case a == 2:
fmt.Println("==2")
case a < 2:
fmt.Println("< 2")
default:
fmt.Println(" ")
}
3.4 跳转到指定代码块标签 goto
4 函数
函数是组织好的 可重复使用的 用来实现单一或相关功能的代码段 可以提高应用的模块性和代码的重复利用率
支持普通函数 匿名函数和闭包
函数属于一等公民
函数可以作为值进行传递
支持匿名函数 和闭包
函数可以满足接口
4.1.1 函数的声明
原型:
func 函数名(参数列表)(返回参数列表){
}
4.1.2 参数简写
func 函数名 (a , b int)(a , b int){}
4.1.3 支持多返回值
func 函数名 (a , b int)(a , b int){}
4.1.4 调用函数
res , err := add(1,1)
4.1.5 示例:将秒解析为时间单位
const p = 60
func resolveTime(s int) (d , h , m int) {
m = s/p
h = s/p/p
d = s/p/p/p
return
}
func main() {
fmt.Println(resolveTime(9000))
}
4.1.6 示例:函数中的参数传递效果测试
type InnerData struct {
a int
}
type Data struct {
complex []int
instance InnerData
ptr *InnerData
}
func passByValue(in Data) Data {
fmt.Printf("打印参数 %+v\n" , in)
fmt.Printf("打印指针 %p\n" , &in)
return in
}
func main() {
in := Data{
complex: []int{1,2,3},
instance: InnerData{
a: 2,
},
ptr: &InnerData{
a:3,
},
}
fmt.Printf("in value :%+v\n" , in)
fmt.Printf("in value :%p\n" , &in)
out := passByValue(in)
fmt.Printf("out value :%+v\n" , out)
fmt.Printf("out value :%p\n" , &out)
}
测试结论
1.值传递
针对值类型是直接复制一份传递,不影响原数据,对内存占用大的数组和结构体,直接传递会占用比较多的资源,建议采用指针传递
2.引用传递
针对应用类型是复制一份指针进行传递,两个指针指向同一片内存空间,所以会修改原有参数
4.2 函数变量
在go语言中 函数也是一种类型,可以和其他类型一样被保存在变量中
var f = func (a , b int) int {
return a + b
}
fmt.Println(f(1 , 3))
4.3 示例:字符串的链式处理
string.ToLower()
。。。。
4.4 匿名函数
func(参数列表)(返回参数列表){
函数体
}
在定义时调用函数
func(a , b int) int {
return a + b
}(1,2)
将匿名函数赋值给变量
var f = func(a , b int)(int){
return a + b
}
f(1,2)
匿名函数用作回调函数
func visit(list []int , f func(int)){
for _ , v := range list {
f(v)
}
}
visit([]int{1,2,3,4,5} , func(v int){
fmt.Println(v)
})
使用匿名函数实现操作封装
var kills = map[string]func(){
"kill" : func() {
fmt.Println("kill")
},
"run" : func() {
fmt.Println("run")
},
}
if f , ok := kills["kill"]; ok{
f()
}else{
fmt.Println("kill no found")
}
4.5 函数类型实现接口
结构体实现接口
type Invoker interface {
Call(interface{})
}
type S struct {
}
func (s *S)Call(i interface{}) {
fmt.Println(i)
}
func main() {
s := &S{}
s.Call("sss")
}
函数实现接口
type F func(interface{})
func (f F) Call(i interface{}) {
f(i)
}
func main() {
var f = F(func(i interface{}) {
fmt.Println(i)
})
f.Call("fff")
}
4.6 闭包 -- 引用了外部变量的匿名函数
在闭包内部修改引用的变量
s := "hello jim"
f := func() {
s = "hello sum"
}
f()
fmt.Println(s)
//hello sum
闭包的记忆效应
func sum(i int)func()int {
return func() int {
i++
return i
}
}
func main() {
s := sum(1)
fmt.Println(s())//2
fmt.Println(s())//3 为什么会是3 因为s匿名函数记录了i的值
s1 := sum(10)
fmt.Println(s1())
}
示例:玩家属性生成器
func playerGen(name string)func()(string , int) {
return func() (string , int) {
//玩家昵称 血量
return name , 150
}
}
func main() {
p := playerGen("p1")
fmt.Println(p())
}
4.7 可变参数
所有参数都是可变参数
func(a ...interface{}){}
部分参数是可变参数
func(a string , b ...interface{}){}
遍历可变参数列表
for k,v := range b {}
获得可变参数的类型
for k,v := range b{
switch v.(type) {
case bool:
typeString = "bool"
case int:
typeString = "int"
}
}
4.8 延迟执行语句 defer
多个延迟语句的处理顺序
func main() {
defer fmt.Println("111")
defer fmt.Println("222")
defer fmt.Println("333")
}
//333
//222
//111
由下至上顺序执行
5 结构体
5.1 定义结构体
type 类型名 struct {
字段1 类型
字段2 类型
}
类型名 标识自定义结构体的名称 在同一个包内不能重复
struct 标识结构体类型 type类型名 struct可以理解为将struct结构体定义为类型名的类型
字段1 字段2 标识结构体的字段名 结构体中的字段名必须唯一
同类型的字段可以写在一行
type ss struct {
a ,b,c int
}
5.2 实例化结构体 -- 为结构体分配内存并初始化
type Test struct {
X , Y int
}
基础实例化形式
var p Test
p.X = 1
Py = 2
创建指针类型的结构体
var p1 = new(Test)
p1.X = 1
p1.Y = 2
取结构体的地址实例化
var p2 = &Test{
1,
2,
}
5.3 初始化结构体的成员变量
键值对初始化结构体
ins := &Test{
X: 1,
Y: 2,
}
使用多个值的列表初始化结构体
ins := &Test{
1,
2,
}
初始化匿名结构体
ins := struct {
X int
Y int
}{
1,
2,
}
5.4 构造函数 -- 结构体和类型的一系列初始化操作的函数封装
type Cat struct {
Name string
Color string
}
func NewCatByName(name string) *Cat {
return &Cat{
Name: name
}
}
func NewCatByColor(color string)*Cat {
return &Cat {
Color: color
}
}
5.5 方法
go语言的结构体方法
type Cat struct {
Name string
Color string
}
func NewCat() *Cat {
return &Cat{
}
}
func (c *Cat)updateColor(color string){
c.Color = color
}
接收器 -- 方法作用的目标
func (接收器变量 接收器类型)方法名(参数列表)(返回参数){
}
接收器变量:接收器中的参数变量名的命名时,官方建议使用接收器类型的第一个小写字符 而不是self this之类
接收器类型:接收器类型和参数类似,可以是指针类型和非指针类型
1.理解指针类型的接收器
指针类型的接收器由一个结构体的指针组成,更接近于面向对象中的this或者self
client := http.Client{}
req , err := http.NewRequest("POST" , "http://www.baidu.com" , strings.NewReader("key=value"))
if err != nil {
return
}
req.Header.Add("aaa" , "vav")
resp , err := client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
data , err := ioutil.ReadAll(resp.Body)
fmt.Println(string(data))
事件注册:
//事件系统需要提供一个外部注册入口 这个入口传入事件名称和对应的事件函数
var eventByName = make(map[string][]func(i interface{}))
//注册消息
func RegisterEvent(name string , callback func(interface{})) {
list , _ := eventByName[name]
list = append(list , callback)
eventByName[name] = list
}
//消费消息
func CallEvent(name string , param string) {
list , ok := eventByName[name]
if !ok {
return
}
for _ , v := range list{
v(param)
}
}
type Login struct {
}
func (l *Login)OnEvent(param interface{}) {
fmt.Println("login event param:" , param)
}
type Reg struct {
}
func (l *Reg)OnEvent(param interface{}) {
fmt.Println("reg event param:" , param)
}
func main() {
//注册事件
l := new(Login)
RegisterEvent("login" , l.OnEvent)
r := new(Reg)
RegisterEvent("reg" , r.OnEvent)
//消费事件
CallEvent("login" , "login")
CallEvent("reg" , "reg")
}
5.6 类型内嵌和结构体内嵌
6 接口
6.1 声明接口
type 接口类型名 interface {
方法名(方法参数)返回值1
方法名2(方法参数)返回值1
}
接口类型名 使用type将接口定义为自定义类型名
方法名:当接口名首字母大写 并且方法名首字母大写时可以被包外的代码访问
type Writer interface {
Write(p []byte)(n int , err error)
}
实现接口的条件:
6.2 实现接口的条件
接口被实现的条件之一:接口的方法和实现接口的类型方法保持一致
type DataWriter interface {
Write(p []byte)(n int , err error)
}
type File struct {
}
func NewFile() *File {
return &File{}
}
func (f *File)Write(p []byte) (n int , err error) {
fmt.Println("写入数据。。。。")
return 0 , nil
}
func main() {
var w DataWriter
f := NewFile()
w = f
n , err := w.Write([]byte("sssssssssssssssss"))
fmt.Println(n , err)
}
接口被实现的条件之二:接口定义的所有方法均被实现
6.3 理解接口和类型的关系
1.一个类型可以实现多个接口
2.多个类型可以实现相同的接口
6.4 便于扩展输出方式的日志系统
6.5使用接口进行数据排序
type Sort interface {
Desc([]byte)[]byte
Asc([]byte)[]byte
}
type BubbleSort struct {
}
type QuickSort struct {
}
6.6 接口的嵌套组合
type Writer interface {
Write(p []byte)(n int , err error)
}
type Closer interface {
Close() error
}
type WriterCloser interface {
Writer
Closer
}
6.7 在接口和类型间转换
类型断言
t , ok := i.(T)
i 代表接口变量
T 代表转换的目标类型
t 代表转换后的变量
将接口转换为其他接口
6.8 空接口类型 -- 能保存所有类型的值
var any interface{}
var a = 1
any = a
fmt.Println(any)
var b = "ssss"
any = b
fmt.Println(b)
//从接口获取值 需要断言
var b string = any.(String)
var a interface{} = 1
var b interface{} = "ss"
fmt.Println(b == a)
//false
6.9 使用空接口实现可以保存任何值的字典
type Dic struct {
Data map[interface{}]interface{}
}
6.10 类型分支 -- 批量判断空接口中变量的类型
switch v.(type) {
case int:
case string:
default:
}
6.11 示例:有限状态机
7 包
7.1工程环境
7.2 创建包package
package 包名
特性如下一个目录下的同级文件属于一个包
包名可以与其目录不同名
包名为main的包为应用程序入口包,边医院吗没有main包时,将无法编译出可以执行文件
7.3 导出标识符
结构体,变量 接口 函数 首字母大写可以包外访问 小写只能包内访问
7.4 导入包 -- 在代码中使用其他包的代码
关键字 import
单行导入 import "包名"
多行导入 import (
"包1"
"包2"
...
)
导入后自定义包名
import (
a "包名1"
)
匿名包导入
import (
_ "包名1"
)
包在程序启动前的初始化入口:init
7.5 示例 工厂模式自动注册
8 并发
8.1 轻量级线程 goroutine -- 根据需要随时创建的线程
关键字 go
格式 go 函数名 (参数列表)
匿名函数的goroutine
go func(参数列表){
}(调用参数列表)
8.2 通道 channel -- 在多个 goroutine 间通信的管道
解决内存静态问题
var 通道名 chan 通道类型
声明后需要make才能使用
var ch = make(chan int , 10)
ch2 := make(chan interface{})
type S struct {
Name string
}
ch := make(chan *S)
使用通道发送数据的格式
<- 往通道内发送数据
-> 从通道内取出数据
1.通道的收发操作在不同的两个goroutine间进行
2.接收将持续的阻塞 直到发送方发送数据
3.每次只接收一个元素
阻塞接收
data := <- ch
非阻塞接收 -- 非阻塞接收可能会造成高CPU占用 因此使用非常少
data , ok := <- ch
阻塞接收数据 忽略从通道返回的数据
<- ch
循环接收数据
for data := range <- ch {
}
单向通道 -- 通道中的单行道
var ch chan<- 元素类型 //定义只能发送的通道
var ch <-chan 元素类型 //定义只能接收的通道
ch := make(<-chan int) //创建只能接收的通道
ch := make(chan<- int) //创建只能发送的通道
带缓冲的通道
ch := make(chan int , 10) 缓冲大小10
阻塞条件:
带缓冲的通道被填满时,尝试再次发送数据时发生阻塞
带缓冲的通道为空时,尝试接收数据时发送阻塞
通道的多路复用 -- 同时处理接收和发送多个通道的数据
go语言中提供了select关键字,可以同时相应多个通道的操作。select的每个case都会对饮搞一个通道的收发过程,当收发完成时就会触发case中相应语句。
select {
case 操作1:
相应操作1
case 操作2:
相应操作2
}
示例:模拟RPC
func RPCClient(ch chan string , req string) (string , error) {
ch <- req
select {
case ack := <- ch://接收服务端返回的数据
return ack , nil
case <- time.After(2*time.Second)://模拟超时
return "", errors.New("Time out")
}
}
func RPCServer(ch chan string) {
for {
data := <- ch
fmt.Println("server received:" , data)
time.Sleep(3 *time.Second)
ch <- "aaa"
}
}
func main() {
ch := make(chan string)
go RPCServer(ch)
re , err := RPCClient(ch , "aaa")
fmt.Println(re , err)
}
延迟打印:
exit := make(chan int)
time.AfterFunc(time.Second , func() {
fmt.Println("aaaaaaaaaaa")
exit <- 0
})
<-exit
定点计时:
timer := time.NewTimer(10 * time.Second)
ticker := time.NewTicker(500 * time.Millisecond)
for {
select {
case <-timer.C:
fmt.Println("timer")
goto HERE
case <-ticker.C:
fmt.Println("ticker")
}
}
HERE:
fmt.Println("aaa")
关闭通道后继续使用通道
1.格式
close(ch)
2.对关闭的通道进行写 会触发panic
3.从已关闭的通道接收数据 将不会发生阻塞
9 反射
9.1 反射的类型对象 (reflect.Type)
使用reflect.TypeOf(int) 可以获得任意值的类型对象
10 编译与工具
10.1 go build 无参数编译
go build 编译main目录下的main函数
10.1.2 go build + 文件列表
go build main1.go main2.go
go build -o main main1.go -o 指定输出文件名
10.1.3 go build + 包
设置gopath后 可以直接根据包名进行编译
go build -o main ./main
10.2 go build 后运行
go run main.go
go run 不能使用 go run + 包的方式进行编译
10.3 编译并安装
go install
10.4 一键获取代码 编译并安装
go get -v ...
go get github.com/dev/....
go get 参数
-v 显示操作流程的日志及信息 方便检查错误
-u 下载丢失的包 但不会更新已存在的包
-d 只下载 不安装
-insecure 允许使用不安装的http方式进行下载
10.5 测试 go test
单元测试文件 必须以_test.go 结尾
运行单元测试命令
go test hello_test.go
运行指定单元测试用例
go test -v -run TestA hello_test.go
10.6 性能分析
go pprof
11 避坑与技巧
11.1 合理地使用并发特性
11.2 反射:性能和灵活性的双刃剑
11.3 接口的nil判断
11.4 map的多键索引 -- 多个数组条件可以同时查询