无缓冲通道:同步通道

//对无缓冲channel类型的发送与接收操作,一定要放在两个不同的Goroutine中进行,否则会导致deadlock。
//无缓冲通道,必须接收和发送都准备好了才能执行。否则会阻塞。
//所以无缓冲通道相当于是同步通道了。
var helloChan = make(chan struct{})

func Test33() {
	fmt.Println("数一下羊吧~~~")
	go SaySheep()
	<-helloChan //这里会暂时阻塞直到SaySheep遍历执行输出10只羊后收到helloChan通道发送的值才会继续下一步
	fmt.Println("you are bad bad")
}

func SaySheep() {
	for i := 0; i < 10; i++ {
		fmt.Println("🐏")
	}
	helloChan <- struct{}{} //空结构体不占用内存
}

输出结果:

有缓冲通道:控制协程、并发的数量

//利用有缓冲通道控制协程的数量,利用waitGroup控制协程的结束
var wg sync.WaitGroup

func Test35() {
	//利用有缓冲通道控制协程数量为3
	ch := make(chan struct{}, 3)
	for i := 0; i < 20; i++ {
		wg.Add(1)
		//这里缓冲通道写满了3个数据后,就会阻塞,等待Weekend方法中发送通道里的数据,才能继续执行
		ch <- struct{}{}
		go Weekend(ch, i)
	}
	wg.Wait()
}

func Weekend(ch chan struct{}, i int) {
	fmt.Println("周末拉", i)
	fmt.Println(" goroutine count = ", runtime.NumGoroutine())
	// time.Sleep(time.Duration(i) * time.Second)
	<-ch
	wg.Done()
}

输出结果:

无缓冲通道也可以控制并发的数量

//无缓冲通道控制协程数量
//思想:开启固定的goroutine池,然后设置无缓冲的通道作为接收任务的中介,固定的协程读取到通道中任务就执行任务。
var wg sync.WaitGroup

func Test36() {
	var cowChan = make(chan string)
	var go_num = 3 //指定协程的数量为3
	for i := 0; i < go_num; i++ {
		go CallCowBoy(cowChan)
	}
	for i := 0; i < 20; i++ {
		SendTask(cowChan, i)
		//也可以直接写在这里
		// wg.Add(1)
		// cowChan <- "🐂🤠" + strconv.Itoa(i)
	}
	wg.Wait()
}

func CallCowBoy(ch chan string) {
	for v := range ch {
		fmt.Printf("%s are very busy \n", v)
		fmt.Println(" goroutine count = ", runtime.NumGoroutine())
		wg.Done()
	}
}

func SendTask(ch chan string, i int) {
	wg.Add(1)
	ch <- "🐂🤠" + strconv.Itoa(i)
}