goroutine特性:
主goroutine 结束,子goroutine退出
package main
import (
"fmt"
"time"
)
func Newtask() {
i := 0
for {
i ++
fmt.Printf("new goroutine i=%d\n",i)//打印输出
time.Sleep(time.Second)//暂停一秒,cpu时间会被其他goroutine获取到。
}
}
// 主goroutine(main函数)
//主goroutine和子goroutine同时在执行,同时获取cpu时间
func main() {
go Newtask()//子goroutine(Newtask)
i := 0
for {
i++
fmt.Printf("main goroutine i=%d\n",i)//打印输出
time.Sleep(time.Second)//暂停一秒,cpu时间会被其他goroutine获取到。
}
}
runtime.Gosched():
出让当前cpu时间片,当再次获得cpu时,从出让位置继续恢复执行。
runtime.Goexit():
return:返回当前函数调用到调用者那里。return之前defer注册生效;
goexit():结束调用该函数的当前goroutine,goexit()之前注册的defer生效。
runtime.GOMAXPROCES:
设置当前进程使用的最大cpu核数,返回上一次调用成功的设置值,首次调用返回默认值。
channle:
一只数据类型,对应一个管道,先进先出。主要用来解决goroutine同步问题以及协程之间的数据共享(数据传递)问题。 goroutine运行在相同的地址空间,通过通信来共享内存。
要求:channel 的读写必须同时满足,才能进行数据流通。
package main
import (
"fmt"
"time"
)
//定义一个全局管道
var ch = make(chan int)
//定义一台打印机
func printer(s string) {
for _,st := range s {
fmt.Printf("%c",st)
time.Sleep(time.Second)
}
}
//定义两个人使用打印机
func person1() {
printer("Hello")
ch <- 8//朝管道中写入8
}
func person2() {
<- ch //读取管道中内容。person1使用打印机完毕钱,一直阻塞
printer("World")
}
func main () {
go person1()
go person2()
for {
;
}
}
无缓冲channel:
通道容量为0,len=0,不能存储数据,channel应用于两个goroutine中,一个读,一个写。必须读写同步。(理解为打电话)
有缓冲channel:
通道容量非0,len(ch) :channel中剩余的未读区的个数,channel应用于两个goroutine中,一个读,一个写。缓冲区可以进行数据存储。存储到容量上限后,才会阻塞。具备异步能力。(理解为发短信,不需要两个goroutine同时在线,不需要同时操作channel缓冲区)
有缓冲区:
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int, 3)
//存满超过3个后,就会阻塞。直到主goroutine读取出这3个数据后,
// 管道为空了,然后会重新写入数据。
go func() {
for i := 0; i < 8; i++ {
ch <- i
//time.Sleep(time.Second)
fmt.Println("子goroutine在执行...i=", i)
}
}()
//启动时刻。主goroutine暂停三秒,因channel是带有缓冲区的,所以并不会发生阻塞现 象。
// 子goroutine继续朝管道中写入数据,并执行打印。三秒后,主gouroutine读取数据,并 打印。
time.Sleep(time.Second * 3)
for i := 0; i < 8; i++ {
num := <-ch
fmt.Println("主goroutine在执行..num=", num)
}
}
关闭channel:
使用close(ch) 关闭channel
对端可以判断channel是否关闭:
遍历channel
1、 if num, ok := <- ch; ok == bool {
如果对端已经关闭channel,那么ok--->false, num无数据
如果对端没有关闭channel,那么ok-->true,num存储读取到的数据。
}
2、for num := range ch {
}
1、数据不发送完,不应该关闭channel。
2、已经关闭的channel不能在里面写数据。
3、已经关闭的channel可以从里面读取 。读取的数据是0
关闭channel:
package main
import (
"fmt"
"time"
)
func main () {
ch := make(chan int)
go func() {
for i := 0; i < 5; i ++ {
ch <- i
fmt.Println("子goroutine正在执行i=",i)
}
close(ch)
}()
time.Sleep(time.Second*3)
for {
if num, ok := <- ch; ok == true {
fmt.Println("主goroutine正在执行,读取的数据是num =",num)
}else {
fmt.Println("主goroutine结束,因为子goroutine已经关闭了channel。")
break
}
}
}
单向channel:
默认双向channel
单向写channel:var writeCh chan <- int writeCh := make(chan <- int)
单向读channel var readCh < - chan int readCh := make(<- chan)
转换:
1、双向channel可以隐士转换为任意一种单向channel。
writeCh = ch
2、单向channel不能转换为双向channel。
传参:传(引用参数)
package main
import "fmt"
func send (out chan <- int) {
out <- 888
close(out)
}
func recv (in <- chan int){
num := <- in
fmt.Println("读到的数据是:",num)
}
func main () {
ch := make(chan int)//双向channel
go func() {
send(ch)//双向channel转换为单向写channel
}()
recv(ch)
}
生产者和消费者模型:
生产者->缓冲区->消费者
缓冲区:1、解藕(降低生产者和消费者的耦合度)2、提高并发 3、缓存
生产者:发送数据端
消费者:接收数据端
代码示例:
package main
import (
"fmt"
"time"
)
//生产者
func producer(out chan <- int) {
for i :=0; i < 10; i++ {
fmt.Println("生产数据:",i * i)
out <- i * i
}
close(out)//记得关闭channel
}
//消费者
func consummer(in <- chan int) {
for num := range in {
fmt.Println("消费数据",num)
time.Sleep(time.Second)
}
}
func main () {
//ch := make(chan int)//无缓冲channel,生产者和消费者同步通信(并行输出)
ch := make(chan int,5)//有缓冲channel,生产者和消费者异步通信(异步输出)
go producer(ch)
consummer(ch)
}
定时器:
创建定时器,指定时常,定时到达后,系统会自动朝定时器的成员C写入当前系统时间(对chan 写操作)
Timer定时器
type Timer struct {
C <-chan Time
r runtimeTimer
}
代码示例:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("当前时间是", time.Now())
timer := time.NewTimer(time.Second * 1)
times := <-timer.C
fmt.Println("读取的时间是", times)
}
Ticker定时器(周期定时 ):
1、 定时时长到达后,系统会自动向Ticker的C中写入系统当前时间,并且每隔一个定时时常后,循环写入系统当前时间。
2、在子goroutine中循环读取C,获取系统时间。
type Ticker struct {
C <-chan Time // The channel on which the ticks are delivered.
r runtimeTimer
}
代码示例:
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
//fmt.Println("now",time.Now())
quit := make(chan bool)
myTicker := time.NewTicker(time.Second)
i := 0
go func() {
for {
nowTime := <- myTicker.C
i ++
fmt.Println("nowTime:",nowTime)
if i == 6 {
quit <- true //解锁主goroutine
runtime.Goexit()//退出
}
}
}()
<- quit //循环获取<- myTicker.C 期间,一直会阻塞。 直到 i=6 秒后有数据后,从管道中读取。
}
输出结果:
nowTime: 2019-12-25 16:34:00.096712652 +0800 CST m=+1.003466126
nowTime: 2019-12-25 16:34:01.098549988 +0800 CST m=+2.005273409
nowTime: 2019-12-25 16:34:02.093829183 +0800 CST m=+3.000522748
nowTime: 2019-12-25 16:34:03.094870846 +0800 CST m=+4.001534381
nowTime: 2019-12-25 16:34:04.094038169 +0800 CST m=+5.000671731
nowTime: 2019-12-25 16:34:05.094124791 +0800 CST m=+6.000728353
Process finished with exit code 0