Go

反射
Golang 反射使用总结

参数传值还是传引用

1
2
3
4
5
6
7
8
9
10
11
func main() {
s := make([]int, 2)
mdSlice(s)
fmt.Println(s)
// 结果:[1 2]
}
func mdSlice(s []int) {
s[0] = 1
s[1] = 2
}

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
var s []int
mdSlice(s)
fmt.Println(s)
// 结果:[]
}
func mdSlice(s []int) {
s = make([]int, 2)
s[0] = 1
s[1] = 2
}

Go语言中所有的传参都是值传递(传值), 都是一个副本,一个拷贝.
因为拷贝的内容有时候是非引用类型(int、string、struct等这些), 这样就在函数中就无法修改原内容数据;
有的是引用类型(指针、map、slice、chan等这些) , 这样就可以修改原内容数据.
Go语言参数传递是传值还是传引用
关于Go中Map类型和Slice类型的传递

类型断言
Type assertions

1
2
3
4
var x interface{} = 7 // x has dynamic type int and value 7
i := x.(int) // i has type int and value 7
i, ok := x.(int) // 7, true
i, ok := x.(string) // , false

1
2
claims := token.Claims.(jwt.MapClaims)
//将Claims转型为jwt.MapClaims

哪些类型可以作为map的key
map键可以是任意类型,只要其值能用 == 运算符比较.
可以用==比较的类型才能作为key, 指针是可以的.
即使指针所指向的条目不能被比较,指针也能直接比较.
这种比较不是基于条目的内容,而是条目的内存地址.
Pointers can be compared for equality even when the items they point to can't be compared. This comparison is not based on the contents of the item, but only on its memory address.

1
2
3
4
myMap := map[regexp.Regexp]string
// invalid map key type regexp.Regexp
myMap := map[*regexp.Regexp]string

int 2 string

1
2
3
4
var t byte=8
var str=""
str+=strconv.Itoa(int(t)) //强制转换int(t), 和java的(int)t不同
fmt.Println(str)

string 2 int

1
2
3
4
5
6
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)

拼接字符串

1
2
3
4
var str="hello"
str+="world"
var strs = []string{"hello", "world"}
fmt.Println(strings.Join(strs, "-"))

函数闭包
从内存回收角度考虑更容易理解闭包,
正常函数调用完后内部的变量就会销毁,但闭包却能使本该销毁的变量一直保留.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
import "fmt"
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
for i := 0; i < 10; i++ {
fmt.Println(
pos(i),
neg(-2*i),
)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main
import "fmt"
func fibonacci() func() int {
prev := 0
curr := 1
return func() int {
next := prev + curr
prev = curr
curr = next
return next
}
}
func main() {
f := fibonacci()
for i := 0; i < 45; i++ {
fmt.Println(f())
}
}

分割字符串

1
2
3
4
5
someString := "one two three four "
words := strings.Fields(someString)
fmt.Println(words)
str := "a b c"
fmt.Println(strings.Split(str, " "))

regex
regex - Split a string on whitespace in Go?

i++和i--
i++和i--在Go语言中是语句,不是表达式,因此不能赋值给另外的变量。此外没有++i和--i.
i++等价于i=i+1

1
2
ret := make(map[string]int)
ret['num']++ //即ret['num']=ret['num']+1

Go语言的“++”和“—”运算符

go的语法可见性为包级别

go doc
go doc builtin.copy
go doc io.Copy

/
除法运算符/的行为则依赖于操作数是否全为整数
5.0/4.0的结果是1.25
5/4的结果是1

len
内置的len函数返回一个字符串中的字节数目

1
2
3
4
5
6
str := "中国"
l1 := len([]rune(str))
l2 := bytes.Count([]byte(str), nil) - 1
l3 := strings.Count(str, "") - 1
l4 := utf8.RuneCountInString(str)
fmt.Println(l1, l2, l3, l4)

slice

1
2
3
4
5
6
7
8
9
10
11
12
13
package main
import (
"fmt"
)
func main() {
var slice1 []int
fmt.Println(slice1[0:]) //[]
slice2 := []int{}
fmt.Println(slice2[0:]) //[]
}
//slice不会越界

给slice追加元素

1
2
3
a:=[]int{1}
a = append(a, 2)
fmt.Println(a)

the syntax to create a slice given an array

1
2
x := [3]string{"abc", "def", "hig"}
s := x[:] // a slice referencing the storage of x

给函数传参,数组传的是拷贝不是引用
因为slice值包含指向第一个slice元素的指针,因此向函数传递slice将允许在函数内部修改底层数组的元素.
换句话说,复制一个slice只是对底层的数组创建了一个新的slice别

数组
数组固定长度,声明数组时必须指定长度,slice不需要.
数组可以用==判等,slice不行,slice唯一合法的比较操作是和nil比较

1
2
3
a:=[...]int{1,2}
b:=[...]int{1,2}
fmt.Println(a==b)

range
如果range左边只有一个变量,该变量是索引

1
2
3
4
5
6
7
8
9
10
11
names := []string{"alice", "charlie"}
for i := range names {
fmt.Println(names[i])
}
ages := map[string]int{
"alice": 31,
"charlie": 34,
}
for name := range ages {
fmt.Println(name)
}

struct
一个命名为S的结构体类型将不能再包含S类型的成员,因为一个聚合的值不能包含它自身.
该限制同样适用于数组.
但是S类型的结构体可以包含*S指针类型的成员.
如果结构体的全部成员都可以用==比较,那么两个该类型的结构体变量就可以使用==比较.
匿名成员:只声明一个成员对应的数据类型而不指定成员的名字.
匿名成员的数据类型必须是命名的类型或指向一个命名的类型的指针.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type Point struct {
X int
Y int
}
type Circle struct {
Point
Radius int
}
var c Circle
c.Radius = 1
c.X = 0
c.Y = 0
fmt.Println(c)

1
2
3
4
5
6
type Demo struct {
text string
}
func main() {
fmt.Println(Demo{"ta"}==Demo{"ta"}) //true
}

手动释放资源
虽然Go的垃圾回收机制会回收不被使用的内存,
但是这不包括操作系统层面的资源,比如打开的文件、网络连接,因此我们必须显式的释放这些资源.

注意
后续的迭代会不断更新dir的值,当删除操作执行时,for循环已完成,dir中存储的值等于最后一次迭代的值.
这意味着,每次对os.RemoveAll的调用删除的都是相同的目录.
应该在循环中用一个局部变量代替dir

1
2
3
4
5
6
7
8
9
10
var rmdirs []func()
for _, dir := range tempDirs() {
os.MkdirAll(dir, 0755)
rmdirs = append(rmdirs, func() {
os.RemoveAll(dir) // NOTE: incorrect!
})
}
for _, rmdir := range rmdirs {
rmdir() // clean up
}

1
2
3
4
5
6
7
8
var rmdirs []func()
dirs := tempDirs()
for i := 0; i < len(dirs); i++ {
os.MkdirAll(dirs[i], 0755) // OK
rmdirs = append(rmdirs, func() {
os.RemoveAll(dirs[i]) // NOTE: incorrect!
})
}

可变参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
fmt.Println(sum(1, 2, 3, 4))
values := []int{1, 2, 3, 4}
fmt.Println(sum(values...)) //原始参数已经是切片类型,传递给sum只需在最后一个参数后加上省略符
}
func sum(vals...int) int {
total := 0
for _, val := range vals {
total += val
}
return total
}

defer、return、返回值的执行顺序
匿名返回值的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {
fmt.Println("a return:", a()) // 打印结果为 a return: 0
}
func a() int {
var i int
defer func() {
i++
fmt.Println("a defer2:", i) // 打印结果为 a defer2: 2
}()
defer func() {
i++
fmt.Println("a defer1:", i) // 打印结果为 a defer1: 1
}()
return i
}

有名返回值的情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func main() {
fmt.Println("b return:", b()) // 打印结果为 b return: 2
}
func b() (i int) {
defer func() {
i++
fmt.Println("b defer2:", i) // 打印结果为 b defer2: 2
}()
defer func() {
i++
fmt.Println("b defer1:", i) // 打印结果为 b defer1: 1
}()
return i // 或者直接 return 效果相同
}

多个defer的执行顺序为“后进先出”;
所有函数在执行RET返回指令之前,都会先检查是否存在defer语句,若存在则先逆序调用defer语句进行收尾工作再退出返回;
匿名返回值是在return执行时被声明,有名返回值则是在函数声明的同时被声明,因此在defer语句中只能访问有名返回值,而不能直接访问匿名返回值;
return其实应该包含前后两个步骤:第一步是给返回值赋值 (若为有名返回值则直接赋值,若为匿名返回值则先声明再赋值) ; 第二步是调用RET返回指令并传入返回值,而RET则会检查defer是否存在,若存在就先逆序插播defer语句,最后RET携带返回值退出函数;
‍‍因此,‍‍defer、return、返回值三者的执行顺序应该是:return最先给返回值赋值; 接着defer开始执行一些收尾工作; 最后RET指令携带返回值退出函数.

1
2
3
4
5
func triple(x int) (result int) {
defer func() { result += x }()
return double(x)
}
fmt.Println(triple(4)) // "12"

return的时候,首先给它赋值为double(x), 然后执行defer语句result+=x, 最后返回result值.
Golang中defer、return、返回值之间执行顺序的坑

方法接收器
不管方法的接收器是指针类型还是非指针类型,都可以通过指针/非指针类型进行调用,编译器会帮你做类型转换.
在声明一个方法的接收器该是指针还是非指针类型时,需要考虑两方面的因素,
第一方面是这个对象本身是不是特别大,如果声明为非指针变量时,调用会产生一次拷贝;
第二方面是如果用指针类型作为接收器,这种指针类型指向的始终是一块内存地址,就算对其进行了拷贝.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type Point struct{ X, Y float64 }
func (p Point) Distance(q Point) float64 {
return math.Sqrt((p.X-q.X)*(p.X-q.X) + (p.Y-q.Y)*(p.Y-q.Y))
}
func (p *Point) ScaleBy(factor float64) {
p.X *= factor
p.Y *= factor
}
func main() {
p := Point{1, 2}
q := Point{4, 6}
distance := Point.Distance // method expression
fmt.Println(distance(p, q)) // "5"
fmt.Printf("%T\n", distance) // "func(Point, Point) float64"
scale := (*Point).ScaleBy
scale(&p, 2)
fmt.Println(p) // "{2 4}"
fmt.Printf("%T\n", scale) // "func(*Point, float64)"
//scale1 := Point.ScaleBy //报错
}

接口

1
2
3
4
5
6
7
8
9
10
11
//func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)
func MySPrintf(format string, args ...interface{}) string {
var buf bytes.Buffer
fmt.Fprintf(&buf, format, args...)
return buf.String()
}
func main() {
fmt.Println(MySPrintf("%f", 2e1))
}

bytes.Buffer中定义了Write(p []byte) (n int, err error)方法,
func (b *Buffer) Write(p []byte) (n int, err error)
bytes.Buffer中的Write方法的接收器是指针b *Buffer, 即*bytes.Buffer实现了io.Writer接口
fmt.Fprintf(&buf, format, args...)中实参也用指针.
接口内嵌

1
2
3
4
type ReadWriter interface {
Reader
Writer
}

空接口interface{}
可以将任意一个值赋给空接口类型变量

1
2
3
var any interface{}
any = true
any = 12.34

nil
nil零值,不同于Java的null, 它既包含类型也包含值
不得不知道的golang知识点之nil
一个包含nil指针的接口不是nil接口
一个接口为nil的充要条件为接口的运行时类型为nil, 并且接口的运行时值为nil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type People interface {
Name() string
}
type Student struct{ name string }
func (stu *Student) Name() string {
return stu.name
}
func getPeople() People {
var stu *Student
return stu
}
func main() {
if getPeople() == nil {
fmt.Println("AAAAA")
} else {
fmt.Println("BBBBB")
}
}
// 输出BBBBB

http.HandlerFunc
函数签名:函数的函数名+形参列表
函数也是值,它们可以像其它值一样传递.
http.HandlerFunc是一个实现了接口http.Handler的方法的函数类型.
ServeHTTP方法的行为是调用了它的函数本身.
因此HandlerFunc是一个让函数值满足一个接口的适配器,这里函数和这个接口仅有的方法有相同的函数签名.

1
2
3
4
5
6
7
8
9
10
11
12
package http
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}

Channels

1
2
3
4
5
6
ch := make(chan int)
x := 1
ch <- x // 发送x变量值到ch
x = <-ch // 从ch接受值并赋值给x
<-ch // 从ch接受值接受并丢弃

两个相同类型的channel可以使用==运算符比较.
如果两个channel引用的是相同的对象,那么比较的结果为真.
一个channel也可以和nil进行比较.
close(ch)用于关闭channel, 随后对该channel的任何发送操作都将导致panic异常.
对一个已经被close过的channel进行接收操作依然可以接受到之前已经成功发送的数据;
如果channel中已经没有数据的话将产生一个零值nil.

不带缓存的Channels

1
2
3
ch = make(chan int) // unbuffered channel
ch = make(chan int, 0) // unbuffered channel
ch = make(chan int, 3) // buffered channel with capacity 3

向无缓存Channels发送数据将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channels上接收数据,
当发送的值通过Channels成功传输之后,两个goroutine可以继续执行后面的语句.
反之,如果接收操作先发生,那么接收者goroutine也将阻塞,直到有另一个goroutine在相同的Channels上发送数据.
基于无缓存Channels的发送和接收操作将导致两个goroutine做一次同步操作.
无缓存Channels也被称为同步Channels.

1
2
3
4
5
6
7
8
9
10
11
12
13
var done = make(chan int)
func loop() {
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
done <- 0 // loop goroutine执行完了,发消息
}
func main() {
go loop()
<-done // main goroutine在此阻塞住,直到取得loop goroutine执行完了的消息
}

死锁
非缓冲信道上如果发生了流入无流出,或者流出无流入,就会导致死锁.
所有goroutine里的非缓冲信道一定要有存有取.

1
2
3
4
5
func main() {
ch := make(chan int)
<- ch // 从信道中取数据,但没有其他goroutine发送数据,main goroutine被一直阻塞
//all goroutines are asleep - deadlock!
}

1
2
3
4
5
func main() {
ch := make(chan int)
ch <- 1 // 1流入信道,但没其他goroutine取走数据,main goroutine会一直阻塞
fmt.Println("This line code wont run") // 在此行执行之前Go就会报死锁
}
1
2
3
4
5
6
7
8
9
10
11
12
var ch1 = make(chan int)
var ch2 = make(chan int)
func say(s string) {
fmt.Println(s)
ch1 <- <-ch2 // ch1等待ch2流出的数据,但没有其他goroutine向ch2发送数据
}
func main() {
go say("hello")
<-ch1 // main goroutine被阻塞
}
1
2
3
4
5
6
7
8
9
10
11
func main() {
ch := make(chan int)
quit := make(chan int)
go func() {
ch <- 1
quit <- 0
}()
<-quit
}
1
2
3
4
5
6
7
8
9
10
11
12
func main() {
ch := make(chan int)
quit := make(chan int)
go func() {
ch <- 1
<-ch
quit <- 0
}()
<-quit
}

main goroutine没有被阻塞,就不会死锁

1
2
3
4
5
6
7
8
9
func main() {
ch := make(chan int)
go func() {
ch <- 1
}()
time.Sleep(5 * time.Second)
}

pipeline channel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
func main() {
naturals := make(chan int)
squares := make(chan int)
// Counter
go func() {
for x := 0; ; x++ { // 如果限定循环次数,就会发生死锁...
naturals <- x
}
}()
// Squarer
go func() {
for {
x := <-naturals // ...因为无法从naturals中取出数据了
squares <- x * x
}
}()
// Printer (in main goroutine)
for {
fmt.Println(<-squares) // ...因为无法从squares中取出数据了
}
}

当一个被关闭的channel中已经发送的数据都被成功接收后,后续的接收操作将不再阻塞,它们会立即返回一个零值.
关闭naturals channel并不能终止循环,它会收到一个永无休止的零值序列,并将它们发送给Printer goroutine

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
func main() {
naturals := make(chan int)
squares := make(chan int)
// Counter
go func() {
for x := 0; x<=10 ; x++ {
naturals <- x
}
close(naturals)
}()
// Squarer
go func() {
for {
x := <-naturals
squares <- x * x
}
}()
// Printer (in main goroutine)
for {
fmt.Println(<-squares)
}
}

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
func main() {
naturals := make(chan int)
squares := make(chan int)
// Counter
go func() {
for x := 0; x <= 10; x++ {
naturals <- x
}
close(naturals)
}()
// Squarer
go func() {
for {
x, ok := <-naturals
if !ok {
close(squares)
break // channel was closed and drained
}
squares <- x * x
}
}()
// Printer (in main goroutine)
for {
x, ok := <-squares
if !ok {
break
}
fmt.Println(x)
}
}

range循环可直接在channels上面迭代,它依次从channel接收数据,当channel被关闭并且没有值可接收时跳出循环.

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
package main
import "fmt"
func main() {
naturals := make(chan int)
squares := make(chan int)
// Counter
go func() {
for x := 0; x <= 10; x++ {
naturals <- x
}
close(naturals)
}()
// Squarer
go func() {
for x := range naturals {
squares <- x * x
}
close(squares)
}()
// Printer (in main goroutine)
for x := range squares {
fmt.Println(x)
}
}

range不等到channel关闭是不会结束读取的,如果缓冲channel干涸了,那么range就会阻塞当前goroutine

1
2
3
4
5
6
7
8
9
10
func main() {
ch := make(chan int, 3)
ch <- 1
ch <- 2
ch <- 3
close(ch) //如果没有关闭channel, 就会死锁
for v := range ch {
fmt.Println(v)
}
}

单方向Channel
类型chan<- int, 发送给channel, 类型<-chan int, 从channel接收

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
func counter(out chan<- int) {
for x := 0; x <= 10; x++ {
out <- x
}
close(out)
}
func squarer(out chan<- int, in <-chan int) {
for v := range in {
out <- v * v
}
close(out)
// close(in) // cannot close receive-only channel
// 关闭操作只用于断言不再向channel发送新的数据
}
func printer(in <-chan int) {
for v := range in {
fmt.Println(v)
}
}
func main() {
naturals := make(chan int)
squares := make(chan int)
go counter(naturals) //类型进行了隐式转换,双向->单向,但单向不能转为双向
go squarer(squares, naturals)
printer(squares)
}

不能关闭receive-only channel

1
2
3
4
5
6
7
chSend:=make(chan <- string) //send-only channel
chReceive:=make(<- chan string) //receive-only channel
chSend <- "abc" //send data to channel
<- chReceive //receive data from channel
close(chSend)
close(chReceive) //cannot close receive-only channel

带缓存的Channels (先进先出)
连续向新创建的channel发送三个值,不会阻塞,如果有第四个发送操作将发生阻塞

1
ch = make(chan string, 3)

注意要将f的值作为一个显式的变量传给函数

1
2
3
4
5
6
7
ch := make(chan struct{})
for _, f := range filenames {
go func(f string) {
thumbnail.ImageFile(f)
ch <- struct{}{}
}(f)
}

下面这样是错误的,这个单独的变量f是被所有的匿名函数值所共享,且会被连续的循环迭代所更新

1
2
3
4
5
6
7
ch := make(chan struct{})
for _, f := range filenames {
go func() {
thumbnail.ImageFile(f)
ch <- struct{}{}
}()
}

select
select 语句类似于switch语句,但是select会随机执行一个可运行的case.
每个case必须是一个通信操作,要么是发送要么是接收.
如果没有case可运行,它将阻塞,直到有case可运行.
一个默认的子句应该总是可运行的.
对一个nil的channel发送和接收操作会永远阻塞,select中nil的channel永远都不会被选中.

sync.WaitGroup
sync.WaitGroup只有3个方法,Add(), Done(), Wait(), 其中Done()是Add(-1)的别名.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var n sync.WaitGroup
func printNum(num int) {
fmt.Println(num)
n.Done() //任务完成,将任务队列中的任务数量-1, 其实Done就是Add(-1)
}
func main() {
for i := 0; i < 10; i++ {
n.Add(1) //每创建一个goroutine, 就把任务队列中任务的数量+1
go printNum(i)
}
n.Wait() //Wait()这里会发生阻塞,直到队列中所有的任务结束就会解除阻塞
fmt.Println("done!")
}

聊天室
客户端访问:nc localhost 8000

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
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadcaster()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}
type client chan<- string // an outgoing message channel
var (
entering = make(chan client)
leaving = make(chan client)
messages = make(chan string) // all incoming client messages
)
func broadcaster() {
clients := make(map[client]bool) // all connected clients
for {
select {
case msg := <-messages:
// Broadcast incoming message to all
// clients' outgoing message channels.
for cli := range clients {
cli <- msg
}
case cli := <-entering:
clients[cli] = true
case cli := <-leaving:
delete(clients, cli)
close(cli)
}
}
}
func handleConn(conn net.Conn) {
ch := make(chan string) // outgoing client messages
go clientWriter(conn, ch)
who := conn.RemoteAddr().String()
ch <- "You are " + who
messages <- who + " has arrived"
entering <- ch
input := bufio.NewScanner(conn)
for input.Scan() {
messages <- who + ": " + input.Text()
}
// NOTE: ignoring potential errors from input.Err()
leaving <- ch
messages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg) // NOTE: ignoring network errors
}
}

monitor goroutine

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
var deposits = make(chan int) // send amount to deposit
var balances = make(chan int) // receive balance
func Deposit(amount int) { deposits <- amount }
func Balance() int { return <-balances }
func teller() {
var balance int // balance is confined to teller goroutine
for {
select {
case amount := <-deposits:
balance += amount
case balances <- balance:
}
}
}
func init() {
go teller() // start the monitor goroutine
}
func main(){
go Deposit(1)
go fmt.Println( Balance())
go Deposit(2)
time.Sleep(1 * time.Second)
fmt.Println( Balance())
}

sync.Mutex互斥锁
go里没有重入锁,没法对一个已经锁上的mutex来再次上锁,这会导致程序死锁.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var (
sema = make(chan struct{}, 1) // a binary semaphore guarding balance
balance int
)
func Deposit(amount int) {
sema <- struct{}{} // acquire token
balance = balance + amount
<-sema // release token
}
func Balance() int {
sema <- struct{}{} // acquire token
b := balance
<-sema // release token
return b
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var (
mu sync.Mutex // guards balance
balance int
)
func Deposit(amount int) {
mu.Lock()
balance = balance + amount
mu.Unlock()
}
func Balance() int {
mu.Lock()
b := balance
mu.Unlock()
return b
}
/*
func Balance() int {
mu.Lock()
defer mu.Unlock()
return balance
}
*/

导入包的重命名

1
2
3
4
import (
"crypto/rand"
mrand "math/rand" // alternative name mrand avoids conflict
)

1
2
3
import "lib/math" // math.Sin
import M "lib/math" // M.Sin
import . "lib/math" // Sin

包的匿名导入
导入一个包但并不使用会导致编译错误,但是有时候只是想利用导入包而产生的副作用.

1
import _ "image/png"

内部包
一个internal包只能被和internal目录有同一个父目录的包所导入.
net/http/internal/chunked内部包只能被net/http/httputil或net/http包导入,但不能被net/url包导入.

1
2
3
4
net/http
net/http/internal/chunked
net/http/httputil
net/url

方法集
T和*T不是一个类型,他们的方法集不同.
类型*T的方法集包含所有 receiver T + *T方法,类型T的方法集只包含所有 receiver T方法.

1
2
3
4
5
6
7
8
9
10
11
12
13
type t1 int
type t2 int
func (t *t1) String() string { return "ptr" }
func (t t2) String() string { return "val" }
func main() {
var a t1
var b t2
a = 1
b = 2
fmt.Println(a, b)
}

atomic原子操作
《GO并发编程实战》—— 原子操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
var sum int32 = 100
var wg sync.WaitGroup
for i := 0; i < 50; i++ {
wg.Add(1)
go func() {
defer wg.Done()
//sum += 1
atomic.AddInt32(&sum, 1)
}()
}
wg.Wait()
fmt.Println(sum)
}

runtime.Gosched()
让当前goroutine让出CPU, 好让其它的goroutine获得执行的机会.
同时,当前的goroutine也会在未来的某个时间点继续运行.

1
2
3
4
5
6
7
8
9
10
11
12
func say(s string) {
for i := 0; i < 2; i++ {
runtime.Gosched()
fmt.Println(s)
}
}
func main() {
runtime.GOMAXPROCS(1)
go say("world")
say("hello")
}

Reader和Writer接口
Read方法最多读取len(p)字节的数据,并保存到p

1
2
3
type Reader interface {
Read(p []byte) (n int, err error)
}

write方法向底层数据流写入len(p)字节的数据,这些数据来自于切片p

1
2
3
type Writer interface {
Write(p []byte) (n int, err error)
}

1
2
3
4
5
6
7
func main() {
u := new([16]byte)
s := u[:]
fmt.Println(s)
i, j := rand.Read(s)
fmt.Println(i, j, u)
}

json
使用json.Unmarshal和json.Decode解码json
数据来自内存中的字符串,使用json.Unmarshal
数据来自流,使用json.Decoder
使用json.Marshal解码json

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
type Post struct {
Title string `json:"title"`
AuthorId int `json:"authorId"`
Content string `json:"content"`
}
func main() {
jsonBlob := []byte( `
{
"title": "test",
"authorId": 1,
"content": "test-content"
}`)
var post Post
//解码 json -> struct
err := json.Unmarshal(jsonBlob, &post)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%+v", post)
//编码 struct -> json
data, err := json.Marshal(post)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data))
//变量的声明必须有新的变量生成,这里不能用:=
data, err = json.MarshalIndent(post, "", " ")
if err != nil {
fmt.Println(err)
}
fmt.Println(string(data))
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Configuration struct {
Address string
ReadTimeout int64
WriteTimeout int64
Static string
}
func main() {
file, err := os.Open("config.json")
if err != nil {
log.Fatalln("Cannot open config file", err)
}
var config Configuration
decoder := json.NewDecoder(file)
err = decoder.Decode(&config)
if err != nil {
log.Fatalln("Cannot get configuration from file", err)
}
}

多次强制转换

1
2
3
4
5
6
7
8
9
10
11
12
13
s := []byte("abcdef")
//[97 98 99 100 101 102]
fmt.Println(s)
a := []byte(s)
//[97 98 99 100 101 102]
fmt.Println(a)
//abcdef
fmt.Println(string(a))
var i int32 = 1
var j = int32(i)
var k = int32(j)
//1
fmt.Println(k)

单例设计模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package singleton
import (
"sync"
)
type singleton struct {
}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}

Singleton Pattern in Go