您的当前位置:首页正文

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

2024-11-29 来源:个人技术集锦

想起之前一道题,两个线程,轮流对同一个数进行加一操作,每个线程执行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)在以下情况下会被判断为死锁:

我们分析上面的代码知道当第二个线程执行完最后一次有一个对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)
}

显示全文