美文网首页
Go基础编程---并发

Go基础编程---并发

作者: addin_gao | 来源:发表于2018-04-13 09:16 被阅读23次

    并行和并发

    并行: 指在同一时刻,有多条指令在多个处理器上同时进行
    并发:同一时刻只能有一条指令执行,但是多个指令被快速的轮换执行,在宏观上体现出过个进程同时执行的效果,微观上是把时间分成若干段,使得多个进程快速交替执行

    goroutine(协程)--- 并发核心

    执行goroutine只要极少的栈内存(4-5kb),自动伸缩,goroutine比thread更易用、高效、轻便

    // 创建一个goroutine : 只要在函数调用语句前添加go 关键字,就可以创建并发执行单元--协程
    func add(){
      for{
        fmt.Println("bbbb")
      }
    }
    func main(){
      go add()
      for{
        fmt.Println("xxxx")
      }
      // 打一部分 xxx 打一部分 bbb
    }
    
    

    主协程退出,其余子协程都将被关闭--有可能主协程退出太快,导致其他子协程没调用就退出

    runtime包

    // runtime.Gosched 短暂让出cpu的时间片,让其他协程执行,不定时后再回到此协程
    func main() {
    
        for i := 0; i < 10; i++ {
            go showNumber(i)
        }
        runtime.Gosched()
        fmt.Println("Haha")
        // 1,0,9, haha
        // 1,2,3,6,haha  每次运行有不同结果
        // runtime.Gosched()是短暂让出cpu的时间片,让其他协程执行,不定时后再回到此协程
    }
    func showNumber (i int) {
        fmt.Println(i)
    }
    
    // runtime.Goexit()   // 退出此协程,return 是退出此函数
    
    // runtime.GOMAXPROCS()  指定以多少核运算,不写默认单核。runtime.GOMAXPROCS(2) 指定2核cpu运算  
    

    多任务资源竞争:

    func main() {
      go printer("hello")
      go printer("world")
      for{}
    }
    func printer (str string) {
      for _,v:= range str{
         fmt.Printf("%c",v)
         time.Sleep(time.Second)
      }
      fmt.Print("\n")   //  hwoelrllod 打印出来这样,每个协程用一会儿就换另外个
    }
    
    // 为了解决多任务资源竞争的问题,需要channel实现
    

    channel数据类型(引用类型),可以用make()创建(用于多个goruntine通讯,内部实现了同步,确保并发安全)

    创建一个channel,
    make(chan Type)  === make(chan Type, 0 ) 无缓冲的channel 读写阻塞
    make(chan Type, cap)  有缓存的channel 无阻塞,满了或者空的才阻塞
    
    channel 数据操作
    channel<-  value  // 发送数据到管道
    <-channel    //管道取出数据丢弃,
    x:= <-channel   // 取出数据赋值给x
    x,ok := <-channel  // 功能同上,同时检测通道是否已经关闭或者是否为空
    
    默认情况下,channel收发数据都是阻塞的,除非另一端准备好,同时接收,这就让gorountine同步变得更加简单,不需要显示的lock
    var chans = make(chan int) // 创建一个通道channel
    func main(){
       go pers("hello")   // 开启新协程
       go pers1("world")  // 开启新协程
       for{}  // 防止主协程退出 
       // world    hello
    }
    func pers(str string){
      <-chans   // 通道取值,没有则阻塞此通道运行
      Printer(str)
    }
    func pers1(str string){
      Printer(str)  
      chans <- 666  // 通道传递值。
    }
    func Printer(str string){
       for _, v := range str{
         fmt.Printf("%c",v)
       }
       fmt.Print("\n")
    }
    
    

    channel 实现同步和数据交互

    func main(){
      var cha = make(chan string)
      go func (){
        for i:=0;i<2; i++{
          fmt.Println(i)
        }
        cha<- "oks"
        fmt.Println("子协程结束")
      }()
      str := <- cha
      fmt.Println(str,"主协程结束")
    }
    

    无缓冲channel

    func main(){
      var cha = make(chan int) //创建无缓冲channel
      go func (){
        for i:=0;i<2; i++{
          cha <- i
          fmt.Println("子协程",i)
        }
        fmt.Println("子协程结束")
      }()
      time.Sleep(time.Second)
      for i:=0;i<2; i++{
        str := <-cha
        fmt.Println("主协程",str)
      }
      fmt.Println("主协程结束")
      /*
      主协程 0
      子协程 0
      子协程 1
      子协程结束
      主协程 1
      主协程结束
       */
    }
    

    有缓冲channel

    func main(){
      var cha = make(chan int,3) //创建有缓冲channel
      go func (){
        for i:=0;i<5; i++{
          cha <- i
          fmt.Println("子协程",i)
        }
        fmt.Println("子协程结束")
      }()
      time.Sleep(time.Second)
      for i:=0;i<5; i++{
        str := <-cha
        fmt.Println("主协程",str)
      }
      fmt.Println("主协程结束")
      /*
      子协程 0
      子协程 1
      子协程 2  // 一次性存三个,然后等一秒
      主协程 0
      主协程 1
      主协程 2  // 一次性取三个。后面的就不确定了
      主协程 3
      子协程 3
      子协程 4
      子协程结束
      主协程 4
      主协程结束
       */
    }
    

    关闭channel

    func main(){
      var cha = make(chan int)
      go func (){
         for i:=0 ;i <4; i++{
           cha<- i
         }
         close(cha)  // 关闭channel,能继续读数据,不能存数据
      }()
      time.Sleep(time.Second)
      for num:=range ch{
          fmt.Println("num=",num)
      }  // 遍历channel,关闭后自动退出遍历
      for{
         if v,oks := <-cha; oks ==true{ //v为值,oks为cha是否关闭,关闭为false
            fmt.Println("主协程",v)
         }else{break}
      }
    }
    

    单向channel特点和应用

    var cha1 chan int   // 正常channel,双向
    var cha2 chan<-  float64  // 单项,只能写入
    var  cha3  <-chan  int   // 单项,只能读取int数据
    双向可以隐式转换为单项channel,反之不能
    
    ch :=make(chan int)  //双向
    var  wirteCh chan<-  int = ch // 只能写,不能读
    var  readCh <-chan  int = ch // 只能读,不能写
    eg:
    func Productor(out chan<- int){ // 参数必须为输入channel
       for i:=0 ;i<10; i++ {
         out<- i*i // 写入
       }
       close(out) // 关闭channel
    }
    func customer(in <-chan int){ // 参数必须为输出channel
       for v := range in{  // 当channel关闭后会自动退出循环
         fmt.Print(v," ")
       }
    }
    func main(){
       ch := make(chan int)
       go Productor(ch)
       customer(ch)
       /*  0 1 4 9 16 25 36 49 64 81  */
    }
    

    定时器Timer 和Ticker

    Timer:一次性定时器

    // Timer实现延时功能
    func main(){
       timer := time.NewTimer(time.Second*4) //创建一个timer,多少秒后会注入到timer的channel里面
       <-timer.C   // 一开始为空值,阻塞程序运行,取到值后继续执行下面
       fmt.Print(timer)
    }
    
    // time.Sleep( time.Second *n) 也能延时 n秒
    
    // <- time.After( 2 * time.Second)  // 定时2s,2s后往channel写内容,返回的是一个管道
    
    // 停止定时器 timer.Stop()
    timer := time.NewTimer(3 * time.Second)
    go  func(){
        <-timer.C
        fmt.Print("xxx")
    }
    timer.Stop() //停止定时器  导致xxx不能打印
    
    // 重置定时器
    timer := time.NewTimer(3* time.Second)
    timer.Reset(1 * time.Second)  // 重新设置为1s, 返回值为bool
    <-timer.C  //去channel的值
    

    Ticker:周期往channel发送一个事件,而channel接收者可以固定时间间隔从channel里面取

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

    select 监测channel上的数据流动

    每个case语句里必须是一个IO操作,在一个select语句中,Go语言会按顺序从头至尾评估每一个发送和接收的语句,如果其中的任意一语句可以继续执行(即没有被阻塞),那么就从那些可以执行的语句中任意选择一条来使用。
    如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况:
    如果给出了default语句,那么就会执行default语句,同时程序的执行会从select语句后的语句中恢复。
    如果没有default语句,那么select语句将被阻塞,直到至少有一个通信可以进行下去。

    func main(){
      // 超时打印
      ch :=make(chan int)
      quit :=make(chan bool)
      go func (){
        for{
          select{
          case num := <-ch:
             fmt.Println(num)
          case <-time.After(5*time.Second): //5秒后添加事件到time的channel事件
             quit <- true  
             fmt.Print("超时quit\n")
          }
        }
      }()
      <-quit
      fmt.Println("主协程退出")
    }
    

    select 实现斐波拉切数列

    func main() {
        // select 实现斐波拉切数列
        ch := make(chan int)    // 数据传递通道
        quit := make(chan bool) // 退出通道
    
        // 使用者
        go func() {
            for i := 0; i < 8; i++ {
                num := <-ch
                fmt.Println(num)
            }
            quit <- true
        }()
        //生产者
        feibo(ch, quit)
    }
    func feibo(ch chan<- int, quit <-chan bool) {
        x, y := 1, 1
        for {
            select {
            case ch <- x:
                x, y = y, x+y
            case <-quit:
                fmt.Print("oks")
                return
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Go基础编程---并发

          本文链接:https://www.haomeiwen.com/subject/iefmcftx.html