go初学:channel引发的死锁问题

本文讲述了在Go语言中,使用通道解决两个线程交替加一操作时遇到的死锁问题,通过检查是否达到结束条件来避免发送数据导致的死锁。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

想起之前一道题,两个线程,轮流对同一个数进行加一操作,每个线程执行10次。

java中使用wait/notify操作,go我想到的是使用两个无缓存channel实现同样的功能

package main

import (
	"fmt"
	"sync"
)

func increment(ch1, ch2 chan struct{}, x *int, id int, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 10; i++ {
		<-ch1
		*x++
		fmt.Println("id :", id, ", x :", *x)
		ch2 <- struct{}{}
	}
    fmt.Println(id, " end")
}

func main() {
	ch1 := make(chan struct{})
	ch2 := make(chan struct{})

	x := 0
	var wg sync.WaitGroup
	wg.Add(2)

	go increment(ch1, ch2, &x, 1, &wg)
	go increment(ch2, ch1, &x, 2, &wg)

	ch1 <- struct{}{}
	wg.Wait()
	close(ch1)
	close(ch2)
}

执行发现功能可以实现,但是最后都会报一个 fatal error: all goroutines are asleep - deadlock! 然后退出。经过网上查询知道:

Go 语言的通道(channel)在以下情况下会被判断为死锁:

  1. 发送数据到通道时没有接收者: 如果你在一个 goroutine 中向通道发送数据,而没有其他 goroutine 在相同的通道上接收数据,那么程序会被判断为发生死锁。

  2. 从通道接收数据时没有发送者: 如果你在一个 goroutine 中从通道接收数据,而没有其他 goroutine 在相同的通道上发送数据,那么程序会被判断为发生死锁。

我们分析上面的代码知道当第二个线程执行完最后一次有一个对ch1的写入操作,这个操作导致了死锁,所以我们要在最后一次的时候停止对ch1的写入操作

package main

import (
	"fmt"
	"sync"
)

func increment(ch1, ch2 chan struct{}, x *int, id int, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 10; i++ {
		<-ch1
		*x++
		fmt.Println("id :", id, ", x :", *x)
		if *x != 20{
			ch2 <- struct{}{}
		}
		
	}
}

func main() {
	ch1 := make(chan struct{})
	ch2 := make(chan struct{})

	x := 0
	var wg sync.WaitGroup
	wg.Add(2)

	go increment(ch1, ch2, &x, 1, &wg)
	go increment(ch2, ch1, &x, 2, &wg)

	ch1 <- struct{}{}
	wg.Wait()
	close(ch1)
	close(ch2)
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值