天天看点

golang chan demo讲解

一、缓冲设置

无缓冲

func testChan() {
	ch := make(chan int)   //  不带第二个参数代表无缓冲,即如果放入了一个数据ch <- task 若没有取出数据的线程task := <-ch,线程将会挂起等待直到数据被取出

	//启动固定数量的worker
	for i := 0; i < 5; i++ {
		go worker(ch)   //  go代表起一个协程并行运行,此处循环多少个,代表起了多少个协程执行任务
	}
	
	//发送任务给worker
	taskList:= []int{88, 99, 111, 222, 333, 555, 666, 234, 544, 777, 888, 265, 152}

	for _, task := range taskList{
		ch <- task   //  无缓冲时每放入一个数据将会等待数据被拿走后循环才能继续
	}

	fmt.Println("process completed!")
	time.Sleep(time.Duration(2) * time.Second)   //  等待子线程打印处理完成
}

func worker(ch chan int) {
	for {
		//接受任务
		task := <-ch   //  取出数据
		fmt.Println("worker:process task = %d", task)
	}
}
           

执行结果:

此处有5个协程在处理任务,各线程包括主线程执行顺序不能保证,所以此处主线程先分发完数据(数据已被子线程取完但没来得及运行到打印部分)

[Running] go run "c:\Code\Go\src\test\main.go"
worker:process task = %d 222
worker:process task = %d 555
worker:process task = %d 88
worker:process task = %d 111
worker:process task = %d 333
worker:process task = %d 99
worker:process task = %d 234
worker:process task = %d 666
process completed!
worker:process task = %d 544
worker:process task = %d 888
worker:process task = %d 777
worker:process task = %d 265
worker:process task = %d 152

[Done] exited with code=0 in 7.046 seconds
           

有缓冲 设置缓冲队列为10

执行结果:

由于缓冲队列为10,可以看到生产队列远没有无缓冲时易被阻塞,不必等协程取数据,可以往队列中连续放置10个数据,很快主线程就先执行完毕

[Running] go run "c:\Code\Go\src\test\main.go"
process completed!
worker:process task = %d 88
worker:process task = %d 333
worker:process task = %d 555
worker:process task = %d 99
worker:process task = %d 544
worker:process task = %d 111
worker:process task = %d 888
worker:process task = %d 265
worker:process task = %d 152
worker:process task = %d 777
worker:process task = %d 666
worker:process task = %d 222
worker:process task = %d 234

[Done] exited with code=0 in 4.734 seconds
           

二、close使用

func main() {
	ch1 := make(chan int, 5)
	ch2 := make(chan int, 5)
	close(ch2)  // 关闭ch2
	go func() { // 启动协程等待channel收到数据
		select {
		case <-ch1: // ch1无数据输入一直阻塞
			log.Printf("case ch1 is here\n")
		case _, ok := <-ch2: // ch2虽然无数据输入,但是ch2通道close了,这里依然能触发,但是ok为false,若是正常输入,这个ok值会为true
			log.Printf("case ch2 is here, ok = %v\n", ok)
			ch1Result := <-ch1 // ch1无数据输入一直阻塞 证明close通道后,chan不会被阻塞
			log.Printf("case ch1 is here ch1Result = %d\n", ch1Result)
		}
	}()

	time.Sleep(time.Duration(5) * time.Second)

	log.Printf("main is here\n")
}
           

执行结果:

[Running] go run "c:\Code\Go\src\test\main.go"
2021/04/07 14:49:02 case ch2 is here, ok = false
2021/04/07 14:49:07 main is here

[Done] exited with code=0 in 8.771 seconds
           

应用:可使用close chan来获取结束信号