Go-Channel

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 的读写必须同时满足,才能进行数据流通。
image
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 {
    ;
}
}
image
image

无缓冲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