Go by Example 中文练习
Posted 蒸汽的兔子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go by Example 中文练习相关的知识,希望对你有一定的参考价值。
文章目录
通道同步
func worker(done chan bool) {
fmt.Println("working...")
time.Sleep(2 * time.Second)
fmt.Println("done")
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
<-done
}
结果:
working...
done
[Finished in 3.1s]
通道选择
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string, 1)
ch2 := make(chan string, 2)
go func() {
time.Sleep(time.Second * 2)
ch1 <- "one"
}()
go func() {
time.Sleep(time.Second)
ch2 <- "two"
}()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
}
}
结果:
$ go run main.go
two
超时处理
func main() {
ch1 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 1)
ch1 <- "1"
}()
select {
case res := <-ch1:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("time out 1")
}
ch2 := make(chan string, 1)
go func() {
time.Sleep(time.Second * 2)
ch2 <- "2"
}()
select {
case res := <-ch1:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("time out 2")
}
}
结果:
$ go run main.go
1
time out 1
非阻塞通道
func main() {
message := make(chan string)
select {
case msg := <-message:
fmt.Print("message", msg)
default:
fmt.Println("no message receive")
}
msg := "1"
select {
case message <- msg: // 当message通道定义一个缓冲区的时候,这里可以执行
fmt.Println("sent message")
default:
fmt.Println("no message sent")
}
}
结果
$ go run main.go
no message receive
no message sent
通道关闭
func main() {
jobs := make(chan int, 5)
done := make(chan bool)
go func() {
for {
j, ok := <-jobs
if ok {
fmt.Println("receive job ", j)
} else {
fmt.Println("receive all jobs")
done <- true // 通知主程序,已经接受全部任务
return
}
}
}()
for i := 1; i < 3; i++ {
jobs <- i
fmt.Println("send job", i)
}
close(jobs)
fmt.Println("send all jobs")
<-done //等待通知
}
结果
$ go run main.go
send job 1
send job 2
send all jobs
receive job 1
receive job 2
receive all jobs
遍历通道
func main() {
queue := make(chan string, 3)
queue <- "one"
queue <- "two"
close(queue)
for elem := range queue {
fmt.Println(elem)
}
}
结果:
$ go run main.go
one
two
定时器
func main() {
timer1 := time.NewTimer(time.Second * 2)
<-timer1.C
fmt.Println("timer 1 expired")
timer2 := time.NewTimer(time.Second * 2)
<-timer2.C
fmt.Println("timer 2 expired")
stop2 := timer2.Stop() // 此时timer2已经倒计时结束了,所以不需要停止
fmt.Println("stop2:", stop2)
if stop2 {
fmt.Println("timer 2 stoped")
}
}
结果:
$ go run main.go
timer 1 expired
timer 2 expired
stop2: false
上面例子中,因为timer2的倒计时已经停止,timer2.stop()没有执行,返回为false,如果想看停止效果,可以改写代码:
func main() {
timer1 := time.NewTimer(time.Second * 2)
<-timer1.C
fmt.Println("timer 1 expired")
timer2 := time.NewTimer(time.Second * 5)
go func() {
<-timer2.C
fmt.Println("timer 2 expired")
}()
stop2 := timer2.Stop()
fmt.Println("stop2:", stop2)
if stop2 {
fmt.Println("timer 2 stoped")
}
}
结果:
$ go run main.go
timer 1 expired
stop2: true
timer 2 stoped
可以看到stop2停止了计时器,程序直接退出了。
也可以使用time自带的after方法实现
func main() {
ch := make(chan string)
go func() {
time.Sleep(time.Second * 2)
ch <- "result"
}()
select {
case res := <-ch:
fmt.Println(res)
case <-time.After(time.Second * 1):
fmt.Println("timeout")
}
}
结果:
$ go run main.go
timeout
计时器
Ticker和timer的区别是,timer倒计时到某一个时间点发送一个信号,而ticker是每隔多长时间发送一个信息,直到我们手动stop
func main() {
ticker := time.NewTicker(time.Second)
go func() {
for t := range ticker.C {
fmt.Println("Tick at ", t)
}
}()
time.Sleep(time.Second * 5)
ticker.Stop()
fmt.Println("ticker stopped")
}
结果:
$ go run main.go
Tick at 2021-05-20 08:55:17.817703 +0800 CST m=+1.003478727
Tick at 2021-05-20 08:55:18.819047 +0800 CST m=+2.004844288
Tick at 2021-05-20 08:55:19.814649 +0800 CST m=+3.000467753
Tick at 2021-05-20 08:55:20.81894 +0800 CST m=+4.004780216
ticker stopped
Tick at 2021-05-20 08:55:21.815348 +0800 CST m=+5.001210115
结果是每隔1秒将当前时间作为值push到通道,然后我们循环这个通道获取值。通过源码可以看到Ticker.C是一个time类型的channel。
源码:
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer
}
工作池
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Println("worker", id, "process job", j)
time.Sleep(time.Second * 2)
results <- j
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// 开启5个进程
for w := 1; w <= 5; w++ {
go worker(w, jobs, results)
}
// 向通道push任务
for j := 1; j <= 9; j++ {
jobs <- j
}
close(jobs)
for r := 1; r <= 9; r++ {
<-results
}
}
result作用是告知主进程执行结束,当所有的执行结束后,主进程结束退出任务,如果没有result可能会导致子进程还没有结束,主进程就退出了。
结果
worker 3 process id 1
worker 1 process id 2
worker 2 process id 3
worker 1 process id 4
worker 2 process id 6
worker 3 process id 5
worker 2 process id 8
worker 1 process id 9
worker 3 process id 7
限速
func main() {
// 限制每2秒执行一次请求
ticker := time.Tick(time.Second * 2)
for i := 1; i <= 5; i++ {
<-ticker
fmt.Println("request", i, time.Now())
}
// 先向burstylimiter push 3个值
limiter := make(chan time.Time, 3)
for i := 0; i < 3; i++ {
limiter <- time.Now()
}
// 然后开启另外一个线程,每2秒向burstylimiter push 一个值
go func() {
for t := range time.Tick(time.Second * 2) {
limiter <- t
}
}()
// 最后实现效果,前三次没有限速,最后两次每2秒执行一次
for i := 1; i <= 5; i++ {
<-limiter
fmt.Println("request", i, time.Now())
}
}
结果:
request 1 2021-05-20 10:09:01.121992 +0800 CST m=+2.005258478
request 2 2021-05-20 10:09:03.117609 +0800 CST m=+4.000918022
request 3 2021-05-20 10:09:05.116884 +0800 CST m=+6.000235109
request 4 2021-05-20 10:09:07.11969 +0800 CST m=+8.003084206
request 5 2021-05-20 10:09:09.119841 +0800 CST m=+10.003278026
request 1 2021-05-20 10:09:09.119978 +0800 CST m=+10.003414895
request 2 2021-05-20 10:09:09.120101 +0800 CST m=+10.003538622
request 3 2021-05-20 10:09:09.12018 +0800 CST m=+10.003616297
request 4 2021-05-20 10:12:29.322124 +0800 CST m=+12.005434486
request 5 2021-05-20 10:12:31.322453 +0800 CST m=+14.005806367
效果:前5次,间隔2s,第6-8次,不间隔时间,9-10次再次间隔2s执行。
互斥锁
func main() {
var state = make(map[int]int)
var mutex = &sync.Mutex{}
for w := 0; w < 10; w++ {
go func() {
for {
key := rand.Intn(5)
val := rand.Intn(100)
mutex.Lock() // 加锁
state[key] = val
mutex.Unlock() // 解锁
}
}()
}
time.Sleep(time.Second)
fmt.Println("state:", state)
}
结果:
$ go run main.go
state: map[0:72 1:25 2:36 3:44 4:38]
当去掉互斥锁配置以后,代码报错,因为同时读写一块内存地址。
go状态协程
func main() {
reads := make(chan *readOp)
writes := make(chan *writeOp)
// 通过select选择来保证同时只能读或写操作
go func() {
var state = make(map[int]int)
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case writes := <-writes:
state[writes.key] = writes.val
writes.resp <- true
}
}
}()
for r := 0; r < 100; r++ {
go func() {
for true {
read := &readOp{
key: rand.Intn(5),
resp: make(chan int),
}
reads <- read
}
}()
}
for w := 0; w < 10; w++ {
go func() {
for {
write := &writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool),
}
writes <- write
<-write.resp
}
}()
}
time.Sleep(time.Second)
}
这里通过select选择来保证同时只有一个读或写操作,这样比通过互斥锁复杂。
排序
func main() {
strs := []string{"c", "a", "b"}
sort.Strings(strs)
fmt.Println(Go by Example 中文练习