go语言基础语法:并发编程和计时器

一、并发编程

1.创建goroutine

func newTask() {
	for {
		fmt.Println("this is the newTask goroutine")
		time.Sleep(time.Second)
	}
}
func main() {
	go newTask()//创建一个新的协程,新建一个任务
	for {
		fmt.Println("this is the main goroutine")
		time.Sleep(time.Second)//延迟一秒,引入头文件"time"
	}
}

go语言基础语法:并发编程和计时器                   go语言基础语法:并发编程和计时器

2.goroutine先退出

如果主协程退出了,那子协程会随之一起结束

//主协程退出了,其他子协程也要跟着一起推出
go func() {
	i := 0
	for {
		i++
		fmt.Println("子协程i=", i)
		time.Sleep(time.Second)
	}
	
}()//调用匿名函数,不要忘记这个()
i := 0
for {
	i++
	fmt.Println("main i=", i)
	time.Sleep(time.Second)
	if i == 2 {
		break
	}
}
fmt.Println("main aaaaa")

go语言基础语法:并发编程和计时器    go语言基础语法:并发编程和计时器    go语言基础语法:并发编程和计时器

3.主协程先退出导致子协程没有来得及调用

func main() {
	go func() {
		i := 0
		for {
			i++
			fmt.Println("子协程 i=", i)
			time.Sleep(time.Second)
		}
	}()
}

没有运行结果

4.GoSched的使用

头文件"runtime"

func main() {
	go func() {
		for i := 0; i < 5; i++ {
			fmt.Println("hello go")
		}
	}()
	for i := 0; i < 2; i++ {
//让出时间片,先让别的协程执行,别的协程执行完,再回来执行此协程
		runtime.Gosched()
		fmt.Println("hello")
	}
}

go语言基础语法:并发编程和计时器

5.Goexit的使用

主函数如下所示

func main() {
//创建新协程
	go func() {
		fmt.Println("aaaaa")
		test()//调用了别的函数
		fmt.Println("bbbbb")
	}()
	for {//写一个死循环,目的是不让主协程结束
	}
}

当test()函数如下:

func test() {
	defer fmt.Println("ccccc")
	fmt.Println("ddddd")
}

结果:go语言基础语法:并发编程和计时器

当test()如下所示: 

func test() {
	defer fmt.Println("ccccc")
	return//终止此函数
	fmt.Println("ddddd")
}

go语言基础语法:并发编程和计时器

当test()如下所示:

func test() {
	defer fmt.Println("ccccc")
	runtime.Goexit()//终止所在的协程,包括调用它的那个协程
	fmt.Println("ddddd")
}

go语言基础语法:并发编程和计时器

6.GOMAXPROCS的使用

runtime.GOMAXPROCS(1)//GOMAXPROCS的参数为指定几核进行运算
for {
	go fmt.Print(1)
	fmt.Print(0)
}

7.多任务资源竞争问题

//定义一个打印机,参数为字符串,按每个字符进行打印
func Printer(str string) {
	for _, data := range str {//遍历字符串
		fmt.Printf("%c", data)
		time.Sleep(time.Second)
	}
	fmt.Println()
}
func person1() {
	Printer("Hello")
}
func person2() {
	Printer("world")
}
func main() {
//新建两个协程,代表两个人,两个人同时使用打印机
	go person1()
	go person2()
	for {//搞一个死循环,不让主协程结束
	}
}

出现资源竞争问题

go语言基础语法:并发编程和计时器

8.通过channel实现同步

var ch = make(chan int)//创建一个channel全局变量(无缓冲)

func Printer(str string) {
	for _, data := range str {
		fmt.Printf("%c", data)
		time.Sleep(time.Second)
	}
	fmt.Println()
}
//当Person1执行完再让person2执行,这样才不会导致打印出来乱码
func person1() {
	Printer("hello")
	ch <- 66//向通道中写数据,发送
}
func person2() {
	<-ch//从通道中取数据,接收,当通道中没有数据的时候,阻塞
	Printer("world")
}
func main() {
	go person1()
	go person2()
	for {
	}
}

go语言基础语法:并发编程和计时器

9.通过channel实现同步和数据交互

//创建channel,无缓冲
ch := make(chan string)
defer fmt.Println("主协程也结束了")
go func() {
	defer fmt.Println("子协程调用完毕")
	for i := 0; i < 2; i++ {
		fmt.Println("子协程 i=", i)	
		time.Sleep(time.Second)
	}
	ch <- "我是子协程,已经工作完毕"
}()
str := <-ch//channel中没有数据的时候,会阻塞
fmt.Println("str=", str)

go语言基础语法:并发编程和计时器

10.无缓冲的channel

ch := make(chan int, 0)//创建一个无缓存的channel
fmt.Printf("len(ch)=%d,cap(ch)=%d\n", len(ch), cap(ch))//len(ch)是缓冲区剩余数据的个数,cap(ch)是缓冲区大小
go func() {//新建协程
	for i := 0; i < 3; i++ {
		fmt.Printf("子协程i=%d\n", i)
		ch <- i//往channel写内容
	}
}()
time.Sleep(time.Second)//延时2秒
for i := 0; i < 3; i++ {
	num := <-ch//通道中的内容,没有内容的时候会阻塞
	fmt.Println("num=", num)
}

go语言基础语法:并发编程和计时器      go语言基础语法:并发编程和计时器

11.有缓冲的channel

ch := make(chan int, 3)//创建一个有缓冲的channel
fmt.Printf("len(ch)=%d,cap(ch)=%d\n", len(ch), cap(ch))
go func() {//新建协程
	for i := 0; i < 10; i++ {
		ch <- i
		fmt.Printf("子协程[%d]:len(ch)=%d,cap(ch)=%d\n", i, len(ch), cap(ch))
	}
}()
time.Sleep(time.Second * 2)
for i := 0; i < 10; i++ {
	num := <-ch
	fmt.Println("num=", num)
}

go语言基础语法:并发编程和计时器   go语言基础语法:并发编程和计时器

12.关闭channel

ch := make(chan int)//创建一个无缓冲的channel
go func() {//创建一个新协程
	for i := 0; i < 5; i++ {
		ch <- i//往通道中写数据
	}
	close(ch)//如果不需要再写数据,关闭channel,关闭后仍可以读管道中的数据,但是无法向管道中写数据
}()
for {
	if num, ok := <-ch; ok == true {//如果ok为true,说明管道中有数据
		fmt.Println("num=", num)
	} else {//管道中无数据
		break
	}
}

go语言基础语法:并发编程和计时器

13.单向channel特性

ch := make(chan int)//创建一个channel,默认为双向
var writeCh chan<- int = ch//只能写,不能读
//双向channel能隐式转换为单向channel
writeCh <- 9//写
//<-writeCh//err
var readCh <-chan int = ch
<-readCh
//readCh<-6//err
//单向无法转换为双向channel
//var ch2 chan int=writeCh//err

14.单向channel的应用

func producer(out chan<- int) {//此通道只能写,不能读
	for i := 0; i < 10; i++ {
		out <- i * i
	}
	close(out)
}
func consumer(in <-chan int) {
	for num := range in {//通道的迭代只有一个参数哦
		fmt.Println("num=", num)
	}
}
func main() {
	ch := make(chan int)//创建一个双向通道
//生产者,生产数字,写入channel
	go producer(ch)//channel传参,以引用形式传递
	consumer(ch)//消费者,从channel读取内容,打印出来
}

go语言基础语法:并发编程和计时器

二、计时器

1.Timer的使用

//创建一个定时器,设置时间为2秒,2秒后会往timer.C通道中写内容(当前时间)
timer := time.NewTimer(2 * time.Second)
fmt.Println("当前时间:", time.Now())
//定时结束后往timer.C中写数据,有数据了就可以读了,没有数据的时候就阻塞
t := <-timer.C
fmt.Println("t=", t)

go语言基础语法:并发编程和计时器

timer := time.NewTimer(time.Second)
for {
	<-timer.C
	fmt.Println("时间到")
}

验证time.NewTimer()时间到了,只会响应一次

go语言基础语法:并发编程和计时器

2.通过Timer实现延时功能

timer := time.NewTimer(2 * time.Second)//延时2秒后打印一句话
<-timer.C
fmt.Println("时间到")
time.Sleep(2 * time.Second)
fmt.Println("时间到")
<-time.After(2 * time.Second)
//定时2秒,阻塞2秒,2秒后产生一个时间,往channel写内容
fmt.Println("时间到")

以上程序都实现一个延时作用

3.停止和重置定时器

timer := time.NewTimer(3 * time.Second)
go func() {
	<-timer.C
	fmt.Println("定时器时间到,子协程开始打印")
}()
timer.Stop()
for {
}

这个程序执行是没有结果的,把定时器停了,那就不会往timer.C通道里面写了,就阻塞在for循环里了

timer := time.NewTimer(3 * time.Second)
ok := timer.Reset(time.Second)//重新设置为1秒
fmt.Println("ok=", ok)
<-timer.C
fmt.Println("时间到")

不需要等3秒了,延时就1秒

4.Ticker的使用

ticker:=time.NewTicker(time.Second)
i:=0
for{
	<-ticker.C
	i++
	fmt.Println("i=",i)
	if i==5{
		ticker.Stop()
		break
	}
}

go语言基础语法:并发编程和计时器

5.使用select构造斐波那契数列

func fibonacci(ch chan<- int, quit <-chan bool) {//ch只写,quit只读
	x, y := 1, 1
	for {
		select {//监听数据流动
		case ch <- x:
			x, y = y, x+y
		case flag := <-quit:
			fmt.Println("flag=", flag)
			return
		}
	}
}
func main() {
	ch := make(chan int)//数字通信
	quit := make(chan bool)//程序是否结束
//消费者,从channel读取内容
//新建协程
	go func() {
		for i := 0; i < 8; i++ {
			num := <-ch
			fmt.Println(num)
		}
		quit <- true//停止信号
	}()
//生产者,产生数字,写入channel
	fibonacci(ch, quit)
}

go语言基础语法:并发编程和计时器

6.通过select实现超时

ch := make(chan int)
quit := make(chan bool)
go func() {
	for {
		select {
		case num := <-ch:
			fmt.Println("num=", num)
		case <-time.After(3 * time.Second)://通道中3秒没有数据就会显示“超时”
			fmt.Println("超时")
			quit <- true
		}
	}
}()
for i := 0; i < 10; i++ {
	ch <- i
	time.Sleep(time.Second)
}
<-quit
fmt.Println("程序结束")

go语言基础语法:并发编程和计时器