[js go]goroutine与channel

199 阅读2分钟

goroutine

go语言天然支持高并发, main函数是主goroutine, 在func前使用go关键字即可开启新的goroutine

hello goroutine

func helloGoroutine() {
	go func() {
		fmt.Printf("hello goroutine \n")
	}()
}

func main () {
	helloGoroutine()
	fmt.Println("main goroutine")
}

运行后发现只打印了main goroutine, 没有打印hello goroutine, 因为开启新goroutine需要耗时, 此时主goroutine已结束运行

func helloGoroutine() {
	go func() {
		fmt.Printf("hello goroutine \n")
	}()
}

func main () {
	helloGoroutine()
	fmt.Println("main goroutine")
    time.Sleep(time.Second)
}

让主goroutine休眠1秒, 即可按预期输出

wait group

实际业务中我们不知道主goroutine需要休眠多久, 此时可用sync.WaitGroup

var wg sync.WaitGroup

func helloGoroutine(index int) {
	defer wg.Done() // 函数结束前执行wg计数器减1
	go func() {
		fmt.Printf("hello goroutine %v \n", index)
	}()
}

func main () {
	helloGoroutine()
	wg.Wait() // 等待所有goroutine全部结束
}

channel

通道是一种类型, 用于goroutine之前通信

	var chanA chan int
	fmt.Printf("type: %T, value: %#v \n", chanA, chanA) // type: chan int, value: (chan int)(nil)
	chanB := make(chan int)
	fmt.Printf("type: %T, value: %#v \n", chanB, chanB) // type: chan int, value: (chan int)(0xc000076060)
	go func(c <-chan int) {                             // 只对chan做读操作, 单向通道
		res := <-c
		fmt.Printf("res: %#v \n", res)
	}(chanB)
	chanB <- 10 // fatal error: all goroutines are asleep - deadlock! 没有接收者
	fmt.Println("send success")

	ch1 := make(chan int)
	ch2 := make(chan int)
	// 开启goroutine将0~100的数发送到ch1中
	go func() {
		for i := 0; i < 100; i++ {
			ch1 <- i
		}
		close(ch1)
	}()
	// 开启goroutine从ch1中接收值,并将该值发送到ch2中
	go func() {
		// 循环从channel中取值
		for {
			i, ok := <-ch1
			if ok {
				ch2 <- i
			} else {
				break
			}
		}
		close(ch2)
	}()
	// 在主goroutine, 循环从channel中取值
	for i := range ch2 { // 通道关闭后会退出for range循环
		fmt.Println(i)
	}

单向通道

有些通道只能接收数据, 有些通道只能发送数据, 可在chan关键字前后加<-符号

    inputChan := make(chan int)
	outputChan := make(chan int)

	// worker池, 这些worker里的goroutine都处理数据
	for workerIndex := 0; workerIndex < 5; workerIndex++ {
		go func(workerIndex int, inputChan <-chan int, outputChan chan<- int) {
			for inputNum := range inputChan {
				fmt.Printf("worker %v, inputNum %v, start \n", workerIndex, inputNum)
				time.Sleep(time.Second)
				fmt.Printf("worker %v, inputNum %v, end \n", workerIndex, inputNum)
				outputChan <- inputNum
			}
		}(workerIndex, inputChan, outputChan)
	}

	for num := 0; num < 5; num++ {
		inputChan <- num
	}
	close(inputChan)
	for i := 0; i < 5; i++ {
		fmt.Printf("outputNum %v \n", <-outputChan)
	}

多路复用

对通道可以尝试发送数据或者接收数据

	// 多路复用
	c := make(chan int, 1)
	for i := 0; i < 10; i++ {
		select {
		case c <- i:
			fmt.Printf("通道尝试接收数据成功, 值为%v \n", i)
		case x := <-c:
			fmt.Printf("通道尝试发送数据成功, 值为%v \n", x)
		default:
			fmt.Println("默认操作")
		}
	}