2021-09-04 最简单的Golang定时器应用以及最简单的协程入门儿

2021-09-04 最简单的Golang定时器应用以及最简单的协程入门儿_第1张图片

  • 什么是协程,作用是什么?
    协程是Go语言实现并发处理的一种方式,说成人话就是在一个程序里同时跑两段代码。比如一个普通的函数 func abc() ,当我们执行abc()的时候,主程序会一直等待abc执行完毕之后再继续,这就是所谓的“阻塞”。很显然,程序是以“串行”方式执行的,效率不高。而如果采用协程的话,则只需要在函数名前面加上 go 这个关键字:go func abc() , 这时候,系统将开辟一个新的空间给函数abc去运行,而不会一直等待它执行完毕,也就是说主程序和函数abc是同时运行的,因此也就实现了“并行”的目的。 这时候,主程序和协程之间就不再是通过传统的传参方式去交互变量了。而是通过一种叫做 “通道” 的东西进行交流。
  • ch := make(chan bool) 是什么意思?
    表示创建一个名为ch的通道,通道中传递的数据类型是布尔型,当然,通道可以传递任意类型,包括自定义类型。譬如说,如果需要在通道中传递字符串类型则:
    ch := make(chan string)
    chan 是Go语言的一种特殊的数据类型,是英文 channel (通道)的缩写,比如以下这条指令:
    v := <-ch
    表示:获取ch 通道的变量并存在v里,注意:通道本身并不是变量的值,但是变量的值可以通过通道传递,通道的一头接主程序,另一头接协程。比如在主程序这边我们输入:
    ch <- true
    意思是说将true这个值发送到通道channel,
    然后在协程这边,我们通过:
    v := <-ch
    从通道中接收从主程序传输过来的值,此时的v就是true.
  • select case是什么?
    用于管理不同的通道,简单地说就是当不同的通道出现新的内容后,该如何处理他们。
  • ticker.C 是什么鬼?
    就是当前时间,Go定时器在time.NewTicker的时候自动产生了一条通道,按照指定的时间间隔(比如500毫秒)向这条通道发送当前时间(如果你把它打印出来,就是一个当前时间值),如果我们的业务需求并不关注当前时间,可以忽略这个值。可以仅仅看作是一条 “ 时间到了 ” 的信号即可。
  • 为什么会出现两个for {} 死循环?
    第一个for {} 死循环出现在协程内部,因为协程开启之后就跟主程序无关了,正常情况下协程执行完毕后return就回来了,但显然定时器这种场合是需要一直保持运转的,因此我们看到在协程内部有一个for {} 死循环,这个循环通过select case{} 持续扫描 channel 通道和ticker.C通道的变化,当通道ticker.C发生变化时,执行我们预先指定好的函数,当channel 通道发生变化时,关闭定时器,退出协程。
    第二个出现在main主函数里面的for {} 死循环就更容易理解了,它让主程序可以持续运行,否则程序一闪而过,那就没法测试了。
package main

import (
	"fmt"
	"time"
)

type Timer struct {
	Interval int     //  设置时间间隔
	Tick     func()	 //  时间到了调用哪个程序?
	Channel  chan bool // 用以和主程序交互的通道
}

// 协程部分:
func (t Timer) Enabled() { 
	ticker := time.NewTicker(time.Duration(t.Interval) * time.Millisecond)
	// 生成一个ticker,到了Intervar指定时间,就执行Tick() ,单位是毫秒
	go func() { // 开启一个协程
		for {	// 协程内是死循环,除非检测到channel通道有内容才会退出
			select {
			case <-t.Channel:	// 检测到来自channel通道有内容,不管内容是什么,直接停止ticker
				ticker.Stop()
				return
			case <-ticker.C: // .C 是ticker内置的一个成员,将当前时间送入ticker通道
				t.Tick()     // 检测到ticker通道有内容后,执行Tick()函数
			}
		}
	}() // 后面这个(),表示立即执行的意思
}

// 主程序部分:
func main() { 
// 用法如下:
	tick1 := func() { // 首先定义两个定时器到时间后需要执行的函数:tick1 和 tick2
		fmt.Print(" A ")
	}
	tick2 := func() {
		fmt.Print(" B ")
	}

	timer1 := new(Timer)
	timer1.Interval = 500	// 定时器1:每500ms触发一次
	timer1.Tick = tick1 // 时间到,调用tick1函数
	timer1.Channel = make(chan bool) // 创建一条通道
	timer1.Enabled() // 启动定时器1

	timer2 := new(Timer)
	timer2.Interval = 1000 
	timer2.Tick = tick2
	timer2.Channel = make(chan bool)
	timer2.Enabled()

// main循环(死)
	for {
		var cmds string
		fmt.Scanln(&cmds) // 检测用户输入
		if cmds == "1" {  
			timer1.Channel <- false  // 关闭定时器1并退出协程
			// 注意: 其实此处无论是true还是false都一样,关键是将值
            // 送入通道,而并不关心值到底是多少。
		}
		if cmds == "2" {
			timer2.Channel <- true // 用true效果一样  
		}
	}
}
  • 以上代码实现的功能:
  1. 产生两个独立的定时器,timer1 间隔时间为500ms, timer2间隔时间为1000ms。
  2. 屏幕上以1秒为一个周期显示字符“A”, 以两秒一个周期显示“B”
  3. 在主界面输入字符“1”,则停止timer1,输入2则停止timer2。
  • 后记
    我感觉注释已经说得很清楚了,如果你对Go协程有点了解的话,上面的代码还是很简单的。关于Golang协程的概念还有很多内容,门道还是有点杂的,本文只起一个最简单的抛砖引玉的作用。

你可能感兴趣的:(Golang,编程,golang)