Go教程

goroutine

本文主要是介绍goroutine,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

goroutine

创建goroutine

package main

import (
   "fmt"
   "time"
)

// 子(从)goroutine
func newTask() {
   i := 0
   for {
      i++
      fmt.Printf("new Goroutine : i = %d\n", i)
      time.Sleep(1 * time.Second)
   }
}

// 主goroutine
func main() {
   //创建一个go程去执行newTask()流程
   go newTask()
   i := 0
   for {
      i++
      fmt.Printf("'main goroutine: i = %d\n", i)
      time.Sleep(1 * time.Second)
   }

}

退出goroutine runtime.Goexit()

func main() {
   //用go创建承载-个形参为空,返回值为空的-一个函数
   go func() {
      defer fmt.Println("A. defer")
      func() {
         defer fmt.Println("B. defer")
         //退出当前goroutine
         runtime.Goexit()
         fmt.Println("'B")
      }()
      fmt.Println("'A")
   }()
   //死循环
   for {
      time.Sleep(1 * time.Second)
   }
}

goroutine中通信

使用channel通信

channel

从channel中读取数据的时候 <-c 中间不可以有空格

无缓冲的

在无缓冲区的情况下会阻塞 会阻塞发送和阻塞接收

import (
   "fmt"
)

func main() {
   //定义一个channel
   c := make(chan int)
   go func() {
      defer fmt.Println("gorout ine结束")
      fmt.Println("goroutine正在运行...")
      c <- 666 //将666发送给c
   }()
   num := <-c //从c中接受数据,并赋值给num
   fmt.Println("num = ", num)
   fmt.Println("main goroutine 结束...")
}

image

在第1步,两个goroutine都到达通道,但哪个都没有开始执行发送或者接收。
在第2步,左侧的goroutine将它的手伸进了通道,这模拟了向通道发送数据的行为。这时,这个goroutine会在通道中被锁住,直到交换完成。
在第3步,右侧的goroutine将它的手放入通道,这模拟了从通道里接收数据。这个goroutine -样也会在通道中被锁住,直到交换完成。
在第4步和第5步,进行交换,并最终,在第6步,两个goroutine都将它们的手从通道里拿出来,这模拟了被锁住的goroutine得到释放。两个goroutine 现在都可以去做其他事情了。

有缓冲的

package main

import (
   "fmt"
   "time"
)

func main() {
   c := make(chan int, 3) //带有缓冲的channel
   fmt.Println("len(c) = ", len(c), ", cap(c)", cap(c))
   go func() {
      defer fmt.Println("子go程结束")
      for i := 0; i < 3; i++ {
         c <- i
         fmt.Println("子go程正在运行,发送的元素是", i, " len(c)=", len(c), ",cap(c)=", cap(c))
      }
   }()

   time.Sleep(2 * time.Second)
   for i := 0; i < 3; i++ {
      num := <-c //从c中接收数据,并赋值给num
      fmt.Println("num = ", num)
   }

   fmt.Println("main结束")
}

image

在第1步,右侧的goroutine正在从通道接收一个值。
在第2步,右侧的这个goroutine独立完成了接收值的动作,而左侧的goroutine正在发送一个新值到通道里。
在第3步,左侧的goroutine 还在向通道发送新值,而右侧的goroutine正在从通道接收另外一个值。这个步骤里的两个操作既不是同步的,也不会互相阻塞。
最后,在第4步,所有的发送和接收都完成,而通道里还有几个值,也有一些空间可以存更多的值。

特点

在管道没有关闭的情况下

当channel已经满,再向里面写数据,就会阻塞

当channel为空,从里面取数据也会阻塞

关闭channel

func main() {
   c := make(chan int)
   go func() {
      for i := 0; i < 5; i++ {
         c <- i
      }
      //close可以关闭-个channel
      close(c)
   }()
   for {
      //ok如果为t rue表示channel没有关闭,如果为false表示channel已经关闭
      if data, ok := <-c; ok {
         fmt.Println(data)
      } else {
         break
      }
   }
   fmt.Println("Main Finished..")
}

channe|不像文件一样需要经常去关闭,只有当你确实没有任何发送数据了,或者你想显式的结束range循环之类的,才去关闭channel;
关闭channel后,无法向channel 再发送数据(引发panic错误后导致接收立即返回零值);
关闭channel后,可以继续从channel接收数据;
对于nil channel,无论收发都会被阻塞。

channel与range

//可以使用range来迭代不断操作channel
for data := range C {
fmt. Println(data)
}

channel与select

单流程下一个go只能监控一个channel的状态, select可以完成监控多个channel的状态

select {
case <- chan1 :
//如果chan1成功读到数据,则进行该case处理语句
case chan2 <- 1:
//如果成功向chan2写入数据,则进行该case处理语句
default:
//如果上面都没有成功,则进入default处理流程
}
func fibonacii(c, quit chan int) {
   x, y := 1, 1
   for {
      select {
      case c <- x:
         //如果c可写,则该case就会进来
         x, y = y, x+y
      case <-quit:
         fmt.Println("quit")
         return
      }
   }
}

func main() {
   c := make(chan int)
   quit := make(chan int)
   //sub go
   go func() {
      for i := 0; i < 10; i++ {
         fmt.Println(<-c)
      }
      quit <- 0
   }()
   //main go
   fibonacii(c, quit)
}
这篇关于goroutine的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!