前言
本文整理了golang面试中比较常见的几个笔试题。
一、控制goroutine数量
题目:实现一个批处理任务,list=[1,2,3,4,5,6,7,8],对list中的元素进行平方运算,要求goroutine数量最多3个。
func power(num int) int {
return num * num
}
func main() {
runner := NewParallelRunner(3)
list := []int{1, 2, 3, 4, 5, 6, 7, 8}
for _, v := range list {
runner.Run(v)
}
runner.Wait()
dict := runner.Result()
var result []int
for _, v := range list {
result = append(result, dict[v])
}
fmt.Println(result)
}
type ParallelRunner struct {
wg sync.WaitGroup
sendQ chan int
result map[int]int
mutex sync.Mutex
}
func NewParallelRunner(parallel int) *ParallelRunner {
sendQ := make(chan int)
runner := &ParallelRunner{
sendQ: sendQ,
result: make(map[int]int),
}
for i := 0; i < parallel; i++ {
runner.wg.Add(1)
go func() {
defer runner.wg.Done()
for v := range sendQ {
runner.mutex.Lock()
runner.result[v] = power(v)
runner.mutex.Unlock()
}
}()
}
return runner
}
func (runner *ParallelRunner) Run(num int) {
runner.sendQ <- num
}
func (runner *ParallelRunner) Wait() {
close(runner.sendQ)
runner.wg.Wait()
}
func (runner *ParallelRunner) Result() map[int]int {
return runner.result
}
二、信号通知
题目:使用两个goroutine,交替打印数字和字母
func main() {
digit := make(chan struct{})
letter := make(chan struct{})
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
startIndex := 1
for {
if _, ok := <-digit; !ok {
break
}
if startIndex >= 10 {
close(letter)
break
}
fmt.Printf("%d", startIndex)
startIndex++
letter <- struct{}{}
}
}()
wg.Add(1)
go func() {
defer wg.Done()
startIndex := 'a'
for {
if _, ok := <-letter; !ok {
break
}
fmt.Printf("%c", startIndex)
startIndex++
digit <- struct{}{}
}
}()
digit <- struct{}{}
wg.Wait()
}
这里的技巧就是:打印数字的goroutine判断终止条件,并关闭打印字母的通道。
三、并发请求
题目:通过goroutine发送多个请求,只要其中一个返回结果,立即结束其他的请求。
func main() {
ctx, cancel := context.WithCancel(context.Background())
recv := make(chan int)
for i := 0; i < 10; i++ {
go func(ctx context.Context, index int) {
ch := make(chan int)
go func() {
// 模拟请求耗时
time.Sleep(time.Duration(rand.Intn(10) * 1000))
fmt.Println("返回结果", index)
ch <- index
}()
res := <-ch
select {
case <-ctx.Done():
break
case recv <- res:
fmt.Printf("gouroutine %d 返回结果\n", index)
break
}
fmt.Printf("gouroutine %d 结束\n", index)
}(ctx, i)
}
v := <-recv
cancel()
fmt.Println(v)
}