You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言水分子生成同步代码死锁原因排查及修复方案

死锁诱因
  • 原子处理函数无限循环阻塞等待组:releaseOxygen、releaseHydrogen内部使用了无限for循环,goroutine启动后永远不会退出,挂载在函数上的defer wg.Done()永远不会执行,导致Make函数中的w.wg.Wait()永久阻塞,后续的结束流程完全无法触发。
  • 条件变量等待逻辑倒置:produceMolecule的等待条件写反,凑齐1个O和2个H的合法生成条件时反而进入等待,没有goroutine负责重置计数、完成分子生成,导致原子处理函数的等待条件永远无法满足,陷入互相等待的死锁。
  • 原子处理逻辑错误:每个releaseHydrogen goroutine对应1个H原子,但代码中每执行一次就往结果里写入HH,会导致结果重复;同时没有设置原子不足时的兜底退出逻辑,多余的O/H会永久等待不存在的原子,进一步加剧死锁。
  • 生成计数逻辑错误:produceMolecule未判断是否凑齐合法原子就直接累加分子数,会导致输出计数不符合预期。
修复方案

核心修改逻辑如下:

  1. 移除原子处理函数的无限循环,每个goroutine仅负责处理1个对应原子,处理完成后直接退出,保证wg.Done()正常执行。
  2. 修正所有条件变量的判断逻辑:
    • produceMolecule只有在凑不够1O2H且未收到结束信号时才进入等待,凑齐原子后完成分子生成、重置计数、广播通知其他goroutine。
    • 原子处理函数等待时加入finished状态判断,收到结束信号后直接退出,避免永久阻塞。
  3. 调整结果写入逻辑,统一在分子生成时写入结果,避免原子处理函数重复写入的问题。
  4. 修正分子计数逻辑,仅凑齐合法原子组合时才累加计数。

修复后的完整代码:

import (
	"sync"
)

// Water 结构体存储解决水分子生成问题所需的同步原语和数据
// moleculeCount 存储已生成的水分子数量
// result 存储原子拼接结果字符串
// wg 等待组用于等待所有goroutine执行完成
type Water struct {
	sync.Mutex
	wg            sync.WaitGroup
	cond          *sync.Cond
	moleculeCount int
	result        string
	assignedO     int
	assignedH     int
	finished      bool
}

// New 初始化Water结构体
func New() *Water {
	water := &Water{
		moleculeCount: 0,
		result:        "",
		assignedO:     0,
		assignedH:     0,
		finished:      false,
	}
	water.cond = sync.NewCond(water)
	return water
}

// releaseOxygen 处理单个氧原子
func (w *Water) releaseOxygen() {
	defer w.wg.Done()
	w.Lock()
	defer w.Unlock()
	w.assignedO += 1
	// 通知生产协程有新原子加入
	w.cond.Broadcast()
	// 等待被消耗或者收到结束信号
	for w.assignedO >= 1 && !w.finished {
		w.cond.Wait()
	}
}

// releaseHydrogen 处理单个氢原子
func (w *Water) releaseHydrogen() {
	defer w.wg.Done()
	w.Lock()
	defer w.Unlock()
	w.assignedH += 1
	// 通知生产协程有新原子加入
	w.cond.Broadcast()
	// 等待被消耗或者收到结束信号
	for w.assignedH >= 2 && !w.finished {
		w.cond.Wait()
	}
}

// produceMolecule 生成水分子
func (w *Water) produceMolecule(done chan bool) {
	for {
		w.Lock()
		// 没凑够1个O+2个H且未结束时进入等待
		for !(w.assignedO >= 1 && w.assignedH >= 2) && !w.finished {
			w.cond.Wait()
		}
		// 收到结束信号直接退出
		if w.finished {
			w.Unlock()
			break
		}
		// 生成水分子
		w.moleculeCount += 1
		w.result += "HOH"
		// 消耗1个O和2个H
		w.assignedO -= 1
		w.assignedH -= 2
		// 通知所有等待的原子协程
		w.cond.Broadcast()
		w.Unlock()
	}
	done <- true
}

func (w *Water) finish() {
	w.Lock()
	w.finished = true
	w.cond.Broadcast()
	w.Unlock()
}

func (w *Water) Molecules() int {
	return w.moleculeCount
}

// Make 接收输入的原子字符串,返回生成的水分子拼接结果
func (w *Water) Make(input string) string {
	done := make(chan bool)
	go w.produceMolecule(done)
	for _, ch := range input {
		w.wg.Add(1)
		switch ch {
		case 'O':
			go w.releaseOxygen()
		case 'H':
			go w.releaseHydrogen()
		}
	}
	w.wg.Wait()
	w.finish()
	<-done
	return w.result
}

内容的提问来源于stack exchange,提问作者Marsh Mellow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 08:36:06