go goroutine 学习笔记(一)

1,170 阅读3分钟
1. goroutine
1.1 goroutine 是什么

goroutine的概念类似于线程,是go语言的核心, goroutine是由Go的运行时(runtime)调度和管理的。

1.2goroutine 的使用

go 中使用 goroutine非常的简单,只需要在函数的前面加上go关键字,就可以为一个函数创建一个 goroutine。

1.3 代码 demo
package main

import (
	"fmt"
	"time"
)

func sayHello() {
	fmt.Println("hello")
}

func main() {
	//开启goroutine 使用 go 关键字
	go sayHello()
	fmt.Println("main end")
	//主线程睡一秒
	time.Sleep(time.Second)

}

2. goroutine 同步
2.1 使用chan 来进行 goroutine 同步

代码 demo

package main

import "fmt"

//使用 chan 来进行 goroutine 同步
func sayHello(c chan int) {
	fmt.Println("hello")
	c <- 1
}

func main() {
    //初始换一个没有缓冲区的管道
	c := make(chan int)
	go sayHello(c)
	fmt.Println("main end")
	<-c

}

2.3 sync.WaitGroup 来实现 goroutine 的同步

sync.WaitGroup 三个方法 Add()、Done()、Wait()。

代码 demo

package main

import (
	"fmt"
	"sync"
)

//声明 sync.WaitGroup
var wg sync.WaitGroup

func sayHello(i int) {
	//
	defer wg.Done()
	fmt.Printf("%d say hello\n", i)
}

func main() {
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go sayHello(i)
	}
	wg.Wait()

}

3. channel

channel 是一种类型,一种引用类型,像一个传送带或者队列,遵循先入先出的规则,保证收发数据的顺序 。用于goroutine间的数据交换。

channel 的声明:var 变量名 chan 类型(var c chan int ),使用 make 初始化,close 关闭。

3.1 无缓存区通道
package main

func main() {
	//无缓冲区通道
	c := make(chan int)
	c <- 1
}

上面的代码运行会报错 :fatal error: all goroutines are asleep - deadlock!

无缓存区的通道必须有人取数据才能存数据。

修改上面的代码,添加一个取数据的 goroutine。

package main

import "fmt"

func getChanData(c chan int) {
	data := <-c
	fmt.Printf("get data :%d\n", data)
}

func main() {
	//无缓冲区通道
	c := make(chan int)
	//启动一个goroutine 获取管道中的数据
	go getChanData(c)
	//关闭管道
	defer close(c)
	c <- 1
	fmt.Printf("end main!")
}

3.2 有缓存的通道

可以直接存放数据,但数据的个数不能超过缓存区的大小。

package main

import "fmt"

func main() {
	//声明一个缓存区为10 的 int 管道
	c := make(chan int, 10)
	for i := 0; i < 10; i++ {
        //向管道存放数据
		c <- i
	}
	fmt.Printf("end main")

}

3.3 单向通道

chan <- 表示单向存数据管道。

<-chan 表示单向取数据管道。

代码 demo

package main

import (
	"fmt"
	"sync"
)

var wg sync.WaitGroup

//取数据
func onlyReadChan(c <-chan int) {
	defer wg.Done()
	data := <-c
	//如果在代码中加入下面的代码,在编译的时候会出现 invalid operation: c <- 12 (send to receive-only type <-chan int)
	//c <- 12
	fmt.Printf("get data: %d\n", data)

}

//写数据
func onlyWriteChan(c chan<- int) {
	defer wg.Done()
	c <- 10
}

func main() {
	c := make(chan int)
	defer close(c)
	wg.Add(2)
	go onlyReadChan(c)
	go onlyWriteChan(c)
	wg.Wait()
	fmt.Printf("end main\n")
}

3.4 遍历通道中的值

普通的for循环需要自己判断是不是没有值了。

package main

import "fmt"

func main() {
	c := make(chan int, 10)
	//存数据
	for i := 0; i < 10; i++ {
		c <- i
	}
	//存完值后要关闭通道,不关闭的话,循环取值的时候会 deadlock
	close(c)
	//取数据
	for {
		i, ok := <-c
		if !ok {
			break
		}
		fmt.Printf("i :%d\n", i)
	}
}

用for range 获取,range 就不需要自己判断,所以推荐用这个方式进行遍历。

package main

import "fmt"

func main() {
	c := make(chan int, 10)
	//存数据
	for i := 0; i < 10; i++ {
		c <- i
	}
    //存完值后要关闭通道,不关闭的话,循环取值的时候会 deadlock
	close(c)
	for v := range c {
		fmt.Printf("v is %d\n", v)
	}
}