Golang中goroutine循环出现的奇怪重复问题

Golang中goroutine循环出现的奇怪重复问题 我有一个函数,实现起来应该相当简单,但我一直被它困扰。这个函数有一个输入通道、一个循环和一个输出通道。我还需要提到,我有一个自定义类型,其中包含一个 gocv mat 以及一些添加的元数据,比如与比较帧相比像素变化的百分比。当函数启动时,会从通道中初始化一个名为 compFrm 的变量,然后循环开始。每次有帧从通道传来时,都会与 compFrm 进行比较以检测运动。如果超过 5 秒,compFrm 就会更新。帧被发送到输出通道,然后循环重新开始。

出于某种原因,每次循环返回时,compFrm 和 procFrm 都完全相同。就好像 compFrm 在每一轮都变成了 procFrm,尽管它本应只在五秒后复制。我已经完全移除了定时器更新的部分,但不知为何它仍然被复制。下面是一个高度简化的版本,但问题依然存在。

我确信这很简单,但我似乎还是搞不定。

func manageUserCamMotion(input chan okoframe.Frame, output chan okoframe.Frame, colorThresh int) {

	var compFrm okoframe.Frame = <-input //Prime the comparison
	var err error

	compTime := time.Now()

	for procFrm := range input {

		procFrm.MotionPercentChange, err = getDiffPerc(procFrm, compFrm, colorThresh)

		if err != nil {
			fmt.Println("Error in manageUserCamMotion:", err)
		}

		//I've tried removing this to see if I was having some kind of malfunction with copying.
		if time.Since(compTime) > 5*time.Second {
			compFrm = procFrm
			compTime = time.Now()
			fmt.Println("Switched")
		}

		output <- procFrm
	}
}

更多关于Golang中goroutine循环出现的奇怪重复问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang中goroutine循环出现的奇怪重复问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


这是一个典型的引用共享问题。okoframe.Frame 很可能包含引用类型(如 gocv.Mat),当您赋值 compFrm = procFrm 时,实际上是在共享底层数据,而不是创建副本。

以下是问题重现和解决方案:

// 问题重现:假设Frame结构包含Mat指针
type Frame struct {
    Mat                *gocv.Mat  // 引用类型
    MotionPercentChange float64
}

// 当执行 compFrm = procFrm 时,两个变量指向同一个Mat对象
// 后续对procFrm.Mat的修改会影响compFrm.Mat

解决方案是创建深层副本:

func manageUserCamMotion(input chan okoframe.Frame, output chan okoframe.Frame, colorThresh int) {
    var compFrm okoframe.Frame = <-input
    var err error
    
    compTime := time.Now()
    
    for procFrm := range input {
        // 创建procFrm的副本用于处理
        currentFrame := procFrm.Clone() // 需要实现Clone方法
        
        currentFrame.MotionPercentChange, err = getDiffPerc(currentFrame, compFrm, colorThresh)
        
        if err != nil {
            fmt.Println("Error in manageUserCamMotion:", err)
        }
        
        if time.Since(compTime) > 5*time.Second {
            // 创建compFrm的独立副本
            compFrm = currentFrame.Clone()
            compTime = time.Now()
            fmt.Println("Switched")
        }
        
        output <- currentFrame
    }
}

需要在 okoframe.Frame 类型中实现 Clone 方法:

func (f *Frame) Clone() Frame {
    // 创建Mat的副本
    matClone := gocv.NewMat()
    f.Mat.CopyTo(&matClone)
    
    return Frame{
        Mat:                &matClone,
        MotionPercentChange: f.MotionPercentChange,
        // 复制其他字段...
    }
}

如果无法修改 okoframe.Frame,可以使用临时解决方案:

func manageUserCamMotion(input chan okoframe.Frame, output chan okoframe.Frame, colorThresh int) {
    var compFrm okoframe.Frame = <-input
    var err error
    
    compTime := time.Now()
    
    for procFrm := range input {
        // 创建当前帧的独立副本
        currentMat := gocv.NewMat()
        procFrm.Mat.CopyTo(&currentMat)
        
        currentFrame := okoframe.Frame{
            Mat:                &currentMat,
            MotionPercentChange: 0,
            // 复制其他必要字段
        }
        
        currentFrame.MotionPercentChange, err = getDiffPerc(currentFrame, compFrm, colorThresh)
        
        if err != nil {
            fmt.Println("Error in manageUserCamMotion:", err)
        }
        
        if time.Since(compTime) > 5*time.Second {
            // 创建compFrm的独立副本
            compMat := gocv.NewMat()
            currentFrame.Mat.CopyTo(&compMat)
            
            compFrm = okoframe.Frame{
                Mat:                &compMat,
                MotionPercentChange: currentFrame.MotionPercentChange,
            }
            compTime = time.Now()
            fmt.Println("Switched")
        }
        
        output <- currentFrame
    }
}

关键点是确保 compFrmprocFrm 不共享任何可变引用类型的数据。每次赋值时都需要创建新的副本,特别是对于 gocv.Mat 这类包含底层数据指针的类型。

回到顶部