Golang中Goroutine和channel的使用指南

Golang中Goroutine和channel的使用指南 我需要一些帮助来理解通道。在下面的代码中,如果我将 court <- 1 移到两个 goroutine(go play…)之前,就会出现死锁。我知道无缓冲通道会阻塞,直到接收和发送同时准备就绪。但是当我把 court <- 1 移到两个 goroutine 之前时,接收部分就在这两个 goroutine 中,我想知道为什么会导致死锁?把 court <- 1 放在两个 goroutine 之前和之后有什么区别?谢谢

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"time"
)

var (
	wg sync.WaitGroup
)
func init(){
	rand.Seed(time.Now().UnixNano())
}

func main(){
	wg.Add(2)
	court := make(chan int)

	go play("James", court)
	go play("Yun", court)

	court <- 1

	wg.Wait()
}

func play(name string, court chan int){
	defer wg.Done()
	for {
		ball, ok := <- court
		if !ok {
			fmt.Printf("player %s won\n", name)
			return
		}
		n := rand.Intn(100)
		if n % 13 == 0{
			fmt.Printf("Player %s missed\n", name)
			close(court)
			return
		}
		fmt.Printf("Player %s Hit %d\n", name, ball)
		court <- ball + 1
	}

}

更多关于Golang中Goroutine和channel的使用指南的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

通过在之前放置,您需要等待通道上有内容发送后才启动 go 协程,这就是发生死锁的原因。

更多关于Golang中Goroutine和channel的使用指南的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你没问题 @NobbZ,我只是在尝试理解 goroutine 中的通道机制…

所以在我看来,无缓冲通道无论是发送数据还是接收数据,都需要在主函数执行前启动 goroutine。否则就会出现死锁。我的理解正确吗?

哦,等等,我完全误解了你的问题……

问题不在于你试图在没有数据可用时读取而导致阻塞,而在于你试图在无缓冲通道上发送数据时,实际上没有可用的接收者。

但基本上效果是一样的。死锁。

我想我该就此打住了,在说更多胡话之前,赶紧给自己找张床休息……

那么当我把这个放在 goroutine 之后,goroutine 中使用的通道也会阻塞该 goroutine,对吗?这些带通道的 goroutine 看起来简单,但实际上我觉得我还是不太明白如何使用它。

我应该在发送到通道之前先使用从通道接收的通道吗?还是相反的方式? 通道应该放在 goroutine 之后吗?

在Golang中,无缓冲通道的发送和接收操作必须同时准备好才能成功执行,否则会导致goroutine阻塞。在你的代码中,将 court <- 1 移到两个goroutine之前会导致死锁,原因如下:

死锁分析:

court <- 1 在goroutine启动之前执行时:

  • 主goroutine尝试向无缓冲通道 court 发送值 1
  • 此时没有其他goroutine在等待接收这个值(两个play goroutine尚未开始执行)
  • 主goroutine被阻塞在发送操作上,无法继续执行
  • 因此两个play goroutine永远不会被调度执行
  • 形成了循环等待:主goroutine等待接收者,但接收者永远不会被创建

正确的执行顺序:

func main(){
	wg.Add(2)
	court := make(chan int)

	go play("James", court)
	go play("Yun", court)

	court <- 1  // 在goroutine启动后发送

	wg.Wait()
}

为什么这样能正常工作:

  • 两个play goroutine先启动,进入循环等待接收通道数据
  • 主goroutine发送 court <- 1 时,已经有goroutine在等待接收
  • 其中一个play goroutine接收到值后继续执行
  • 程序正常进行乒乓球模拟

示例演示阻塞情况:

// 会导致死锁的版本
func main(){
	court := make(chan int)
	
	court <- 1  // 阻塞在这里,没有接收者
	
	go func() {
		<-court  // 这个goroutine永远不会执行
	}()
}

在你的代码中,play函数的设计要求通道两端都有goroutine准备就绪:一个发送,一个接收。将发送操作提前破坏了这种准备就绪的平衡,导致所有goroutine都无法继续执行。

这就是无缓冲通道的同步特性:发送和接收必须成对出现且同时准备好。

回到顶部