Skip to content

Instantly share code, notes, and snippets.

@noname007
Created July 2, 2024 10:05
Show Gist options
  • Save noname007/6fd0a23920fa115618dbef2504511727 to your computer and use it in GitHub Desktop.
Save noname007/6fd0a23920fa115618dbef2504511727 to your computer and use it in GitHub Desktop.
Golang 面试题 (4) N个协程交替打印1-100
启动N个协程,共用一个外部变量计数器,计数器范围是1到100
开启N个有缓冲chan,chans[i]塞入数据代表协程i可以进行打印了,打印的数字就是计数器的数
协程i一直阻塞,直到chan[i]通道有数据可以拉,才打印
@noname007
Copy link
Author

copy https://www.cnblogs.com/yinbiao/p/16142008.html

func main() {
	gorutinenum := 5
	var chanslice []chan int
	exitchan := make(chan int)

	for i := 0; i < gorutinenum; i++ {
		chanslice = append(chanslice, make(chan int, 1))
	}

	res := 1
	j := 0
	for i := 0; i < gorutinenum; i ++ {
		go func(i int) {
			for {
				<-chanslice[i]
				if res > 100 {
					exitchan <- 1
					break
				}

				fmt.Println(fmt.Sprintf("gorutine%v:%v", i, res))
				res ++

				if j == gorutinenum-1 {
					j = 0
				}else {
					j ++
				}
				chanslice[j] <- 1
			}
		}(i)
	}
	chanslice[0] <- 1

	select {
	case <-exitchan:
		fmt.Println("end")
	}
}

@noname007
Copy link
Author

noname007 commented Jul 2, 2024

与上面的代码一样,存在协程资源泄露问题,没有做回收

package main

import (
	"fmt"
	"sync"
)

//N个协程交替打印数字,到达一个数值后终止
//
//解法:
//启动N个协程,共用一个外部变量计数器,计数器范围是1到100
//开启N个无缓冲chan,chans[i]塞入数据代表协程i可以进行打印了,打印的数字就是计数器的数
//协程i一直阻塞,直到chan[i]通道有数据可以拉,才打印
//

var counter = 0

func DoJob(chans []chan int64) {
	chanLen := len(chans)
	quitCh := make(chan int)

	wg := sync.WaitGroup{}

	for i := 0; i < chanLen; i++ {
		go func(idx int) {
			wg.Add(1)
			defer wg.Done()

			for {
				<-chans[idx]
				if counter > 100 {
					quitCh <- 1
					break
				}
				fmt.Printf("goroutine-num:%d %d\n", idx, counter)
				counter += 1
				chans[(idx+1)%chanLen] <- 1
			}
		}(i)
	}

	chans[0] <- 1

	<-quitCh
	wg.Wait()
}

func main() {
	//chanNum := 1  //panic
	chanNum := 10
	chans := make([]chan int64, chanNum)

	for i := 0; i < (chanNum); i++ {
		chans[i] = make(chan int64)
	}

	DoJob(chans)
}

@noname007
Copy link
Author

noname007 commented Jul 2, 2024

package main

import (
	"fmt"
	"sync"
)

//N个协程交替打印数字,到达一个数值后终止
//
//解法:
//启动N个协程,共用一个外部变量计数器,计数器范围是1到100
//开启N个无缓冲chan,chans[i]塞入数据代表协程i可以进行打印了,打印的数字就是计数器的数
//协程i一直阻塞,直到chan[i]通道有数据可以拉,才打印
//

var counter = 0

const CounterMaxValue = 100

func DoJob(GoRoutineNum int) {

	//用于控制业务逻辑
	sigChannel := make([]chan struct{}, GoRoutineNum)

	//用于控制协成是否终结
	quitSigChannel := make([]chan struct{}, GoRoutineNum)

	for i := 0; i < (GoRoutineNum); i++ {
		sigChannel[i] = make(chan struct{})
		quitSigChannel[i] = make(chan struct{})
	}

	mainRoutine := make(chan struct{})

	wg := sync.WaitGroup{}

	for i := 0; i < GoRoutineNum; i++ {
		wg.Add(1)
		go func(idx int) {
			defer wg.Done()
			//FOR:
			for {
				select {
				case <-quitSigChannel[idx]:
					//break FOR
					goto END
				case <-sigChannel[idx]:
					fmt.Printf("goroutine-num:%d %d\n", idx, counter)
					counter += 1
					mainRoutine <- struct{}{}
				}

			}
		END:
			fmt.Printf("goroutine-num:%d end\n", idx)
		}(i)
	}

	wg.Add(1)

	go func() {
		wg.Done()

		for i := 0; i < CounterMaxValue; i++ {
			//<-mainRoutine
			sigChannel[i%GoRoutineNum] <- struct{}{}
			<-mainRoutine
		}

		////使最后一个任务处理协成,进入信号等待状态
		//<-mainRoutine

		for i := 0; i < GoRoutineNum; i++ {
			quitSigChannel[i] <- struct{}{}
		}
	}()

	//mainRoutine <- struct{}{}
	wg.Wait()
}

func main() {
	DoJob(10)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment