我是go的初学者,我今天写了一个问题代码。本意是让资源生成和消费的时候同时打印出该资源信息。 但是该程序会导致死锁,有没有什么解决方法能保证输出信息和管道同步
var mu sync.Mutex
func produce(ch chan<- int){
for i:=0;i<10;i++{
mu.Lock()
ch<-i
fmt.Println("produce:"+strconv.Itoa(i))
mu.Unlock()
}
}
func consumer(ch <- chan int){
for i:=0;i<10;i++{
mu.Lock()
v:=<-ch
fmt.Println("consumer:"+strconv.Itoa(v))
mu.Unlock()
}
}
func main(){
ch:=make(chan int,5)
go produce(ch)
go consumer(ch)
time.Sleep(10*time.Second)
}
有疑问加站长微信联系(非本文作者)

个人理解不一定正确: produce在执行第五次的时候,ch已经执行mu.Lock(),此时ch已满,通道阻塞,但是mu.Unlocl没有释放锁,所以造成了持续阻塞。
谢谢解答。不过我的疑惑是produce在通道未满的时候,也有释放锁。但是consumer永远竞争不到,produce一直占有
小兄弟注意哈,使用go关键字 以goroutine方式执行后面的代码,就不能根据父协程的代码顺序来看谁先执行了。所以 go produce(ch) go consumer(ch) 这两行代码不能说明produce先被执行。更离谱的是,CPU使用单核执行的前提下,大多数机子上可能先执行consumer。
刚运行了一下,原因是正如3楼所说的,consumer(ch)早于produce(ch)先执行, consumer(ch)上来先拿到mu.Lock然后立马阻塞在了v:=<-ch, 而produce(ch)执行的时候一直拿不到mu.Lock,所以导致produce(ch)无法生产, consumer(ch)干等着没什么可消费的。你把go consumer(ch) go produce(ch)顺序换一下就不一样了
由于consumer里面chan没有东西而进行堵塞,导致锁无法释放!进行了饥饿状态了。如果要解决建议使用 chan 对其进行拦截,就可以实现同步了
package main
import ( "fmt" "strconv" "sync" "time" )
var mu sync.Mutex
func produce(ch chan<- int,index chan <-int){ for i:=0;i<10;i++{ index <- 1 ch<-i fmt.Println("produce:"+strconv.Itoa(i)) } }
func consumer(ch <- chan int,index <- chan int){ for i:=0;i<10;i++{ v:=<-ch fmt.Println("consumer:"+strconv.Itoa(v)) <-index } } func main(){ ch := make(chan int,5) index := make(chan int,1) go produce(ch,index) go consumer(ch,index) time.Sleep(10*time.Second) }
chan不需要容量呗
或者chan只有一个容量也可以,保证放之前,肯定被拿了
回答下我的浅薄建议,为什么要把chan和sync.Mutex一起使用尼,这种场景见过嘛。这样写,不是一样的嘛
func produce(ch chan<- int) { for i := 0; i < 10; i++ { ch <- i fmt.Println("produce:" + strconv.Itoa(i)) } }
func consumer(ch <-chan int) { for i := 0; i < 10; i++ { v := <-ch fmt.Println("consumer:" + strconv.Itoa(v)) } }
过度设计了吧,个人觉得
老哥我说了我的本意是让资源生成和消费的时候同时打印出该资源信息。
你可以换一种方式,返回通道。
个人理解,你在对通道元素的发送、接收外部封装了锁,确实保证了同一时间要么在向通道写元素,要么在从通道里面读元素,但是由于发送/接收操作都在go routine中实现,因此两种情况会出现死锁: