出现场景

主线程创建并初始化 channel ,开启多线程操作 k8s 创建 pvc、pv,主线程阻塞;程序在线程 xx 中创建 pvc 失败,线程报错,将错误信息输入到 channel 中,主线程接收到 channel 并报错panic: send on closed channel [recovered] panic: send on closed channel

简化代码

package main

import (
	"errors"
	"fmt"
	"time"
)

func main() {
	test()
	// 主线程阻塞,防止主线程过早结束
	select {}
}

func test() error {
	strChan := make(chan string)
	defer close(strChan)
	for i := 0; i < 2; i++ {
		tmp := i
		go func(i int) {
			defer func() {
				if err := recover(); err != nil {
					fmt.Println("recover...")
					strChan <- "revocer 检测到错误"
					return
				}
			}()
			if i == 1 {
				time.Sleep(2 * time.Second)
			}
			strChan <- fmt.Sprintf("i am %v goroutine", i)
			return
		}(tmp)
	}
	select {
	case a := <-strChan:
		if a != "" {
			return errors.New("检测到管道中数据,停止阻塞")
		}
	}
	fmt.Println(<-strChan)
	return nil
}

原因分析

主线程使用 for 循环开启两个协程,假设为协程 0、1,
执行协程 0,将“i am 0 goroutine”字符串输入到 strChan 中,
执行协程 1,协程1等待2秒钟后执行上述操作,
主线程执行 select 阻塞等待,此时协程0将数据输入到管道 strChan 中,
主线程中的 case a := <-strChan 接收到数据,准备执行 return 操作,在执行 return 之前程序会先执行 defer close(strChan) 关闭通道,
所以,协程0执行完毕以后 strChan 管道已经被关闭。
两秒钟以后协程1往 strChan 中输入数据,此时管道已关闭,发生 panic 错误并被 recover 检测到。

总结:
多个线程之间存在时间差,有的线程过早执行完毕触发代码的关闭管道操作,导致程序发生 panic。

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐