go 编程笔记二

目录

并发 Goroutine

Channel

  • channel 是引 类型, 使 “make(chan )” 创建, 是要传递的数据类型。
  • channel 使 “<-“ 接收和发送数据。
  • “chan <- data” 发送数据,
  • “data <- chan” 接收数据,
  • 可以 忽略掉接收结果, 纯粹作为同步信号通知。默认情况下, 发送和接收都会被阻塞 (block)。
  • 发送操作被阻塞, 直到接收端准备好接收。
  • 接收操作被阻塞, 直到发送端准备好发送。

创建 channel 使用 make(chan bool)
, 执行 并发 使用 哦 go method()

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

package main

import (
"runtime"
)

func test(c chan bool, b bool) {
x := 0
for i := 0; i < 100000000; i++ {
x += i
}
println(x)
if b {
c <- true
}
}
func main() {
// 告知调度器同时使 多个线程
runtime.GOMAXPROCS(2)
c := make(chan bool)
for i := 0; i < 100; i++ {
go test(c, i == 99)
}
<-c
}



package main

import "time"

func test1(c chan int) {
time.Sleep(3 * time.Second)
println("go...")
c <- 1
}
func main() {
c := make(chan int)
go test1(c)
println("hi!")
<-c // 阻塞等待退出信号, 忽略掉返回的数据。
println("over!")
}
  • 接收端使 迭代器或者循环接收数据, 则必须调 close . 否则可能导致接收端 throw: all goroutines are asleep - deadlock!。
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() {
c := make(chan int)
go func() {
for i := 0; i < 20; i++ {
c <- i
}
close(c) // 如果不关闭, 会引发接收端 throw: all goroutines are asleep - deadlock! }()
for v := range c {
println(v)
}
}
}


func main() {
c := make(chan int)
go func() {
for i := 0; i < 20; i++ {
c <- i
}
close(c)
}()
for {
if v, ok := <-c; ok {
76
println(v)
} else {
break;
}
}
}

单向通道

  • 可以将 channel 指定为单向通道。 如 “<-chan int” 仅能接收, “chan<- int” 仅能发送。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

func recv(c <-chan int, over chan <- bool) {
for v := range c {
println(v)
}
over <- true
}
func send(c chan <- int) {
for i := 0; i < 10; i++ {
c <- i
}
close(c) // 省略 close, 可能会导致 recv throw: all goroutines are asleep - deadlock!
}
func main() {
c := make(chan int) // 双向 channel, 可以转换为单向 channel。
o := make(chan bool)
go recv(c, o)
go send(c)
<-o
}

异步通道

异步通道其实就是给 channel 设置一个缓存区
如: make(chan int, 10)

1
2
3
4
5
6
7
8
9
10
11
func main() {
c := make(chan int, 10)
o := make(chan bool)
go func() {
time.Sleep(2 * time.Second)
println("recv:", <-c)
o <- true
}()
c <- 100 // 未阻塞 println("send over...")
<-o
}

Select

  • 随机处理 个可 的 channel。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func main() {
c1, c2 := make(chan int), make(chan string)
o := make(chan bool)
go func() {
for {
select {
case v, ok := <-c1:
if !ok {
o <- true; break
}
println("c1 =", v)
case v, ok := <-c2:
}
}
}()
c1 <- 1
c2 <- "a"
c2 <- "b"
c1 <- 2
close(c1)
//close(c2)
<-o
}

Timeout

time 包还提供了 After、Tick 等函数返回计时器 channel。
如下 的例 中, 我们 After 来处 理 个需求: 不管有没有数据操作, 总之 5 秒后终 循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

func main() {
c := make(chan int)
o := make(chan bool)
tick := time.Tick(1 * time.Second)
go func() {
for {
select {
case v := <-c:
println(v)
case <-tick:
println("tick")
case <-time.After(5 * time.Second):
// 只用一次, 没必要用外部变量。
println("timeout")
o <- true
break
}
}
}()
<-o
}

测试

  • 测试函数名称必须是 “Test[^a-z]” 这样的格式,如下:TestSum. 测试文件必须是 _test.go .

  • 在使用 idea 进行测试的时候,需要配置需要测试的文件或者 package。

  • 使用 命令测试 :go test
  • go test -v ./src/test/…
  • • -v: 显 所有测试函数运 细节。
  • • -run=regex: 指定要执 的的测试函数。
  • main_test.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

package main

import (
"testing"
)

func sum(args... int) int {

sum := 0

for _, v := range args {
sum += v
}
return sum
}
func TestSum(t *testing.T) {
if sum(1, 2, 3) != 16 {
t.Log("测试样例失败!")
t.FailNow()
}
}

Benchmak 压力测试

  • Benchmak 的使用和 test 类似。 函数名使用 Benchmak 开头

  • 命令的使用如下:go test -v ./src/test/… -bench ^BenchmarkSum$ -run ^$

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

package main

import (
"testing"
)

func sum(args... int) int {

sum := 0

for _, v := range args {
sum += v
}
return sum
}
func TestSum(t *testing.T) {
if sum(1, 2, 3) != 16 {
t.Log("测试样例失败!")
t.FailNow()
}
}

func BenchmarkSum(b *testing.B) {
for i := 0; i < b.N; i++ {
sum(1, 2, 3)
}
}