Go by Example 中文练习

Posted Parker@1989

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("Strings:", strs)

	ints := [以上是关于Go by Example 中文练习的主要内容,如果未能解决你的问题,请参考以下文章

Go by Example 中文练习

Go by Example-变量

go语言实现登录注册收藏相关工具和教程链接

Go by Example

go语言从例子开始之Example32.打点器

Go指南练习_循环与函数