Golang中main函数死锁错误的解决方法
Golang中main函数死锁错误的解决方法 你好, 我是一名学生,学习Go语言已经三周了。我的代码在这里遇到了问题。
你能帮助我吗?
我需要创建一个包含节点的图,并在最后找出领导者(最大ID)。
package main
import (
"container/list"
"fmt"
"sync"
"time"
)
func Max(x, y int) int {
if x < y {
return y
}
return x
}
type Message struct {
Id int
IsLeader bool
}
type GraphNode struct {
edges *list.List
localId int
leaderId int
synChannel <-chan int
}
type bidirectionalEdge struct {
inChannel <-chan Message
outChannel chan<- Message
fromId int
toId int
}
var wg sync.WaitGroup
func NewGraphNode(id int, synChannel <-chan int) *GraphNode {
return &GraphNode{list.New(), id, -3, synChannel}
}
func (node *GraphNode) addEdge(edge bidirectionalEdge) {
node.edges.PushBack(edge)
}
func (edge bidirectionalEdge) listen(result *int) {
fmt.Println("### Listening ", edge.fromId, ">", edge.toId)
last_max := -1
for receivedMessage := range edge.inChannel {
fmt.Println("<<<", edge.fromId, "- Received:", receivedMessage.Id, receivedMessage.IsLeader)
max := Max(edge.fromId, receivedMessage.Id)
if max > last_max {
last_max = max
edge.outChannel <- Message{max, false}
fmt.Println(">>>", edge.fromId, "- Sent (", max, ") To", edge.toId)
}
}
if last_max > *result {
*result = last_max
}
return
}
// Main code for all nodes
func (n *GraphNode) Run(wg *sync.WaitGroup) {
// Done will be executed once the for loop is over, i.e., the inChannels is closed
defer wg.Done()
synCode := <-n.synChannel
fmt.Println("*", n.localId, "- Step #", synCode, "Initialization")
// Inialisation: every node sends its id to the next node on the ring
for e := n.edges.Front(); e != nil; e = e.Next() {
e.Value.(bidirectionalEdge).outChannel <- Message{n.localId, false}
fmt.Println(">>>", e.Value.(bidirectionalEdge).fromId, "- Sent (", n.localId, ") To", e.Value.(bidirectionalEdge).toId)
}
time.Sleep(time.Second)
synCode = <-n.synChannel
fmt.Println("*", n.localId, "- Step #", synCode, "Listening")
result := -2
for e := n.edges.Front(); e != nil; e = e.Next() {
go e.Value.(bidirectionalEdge).listen(&result)
}
time.Sleep(time.Second)
synCode = <-n.synChannel
fmt.Println("*", n.localId, "- Step #", synCode, "Closing channels")
for e := n.edges.Front(); e != nil; e = e.Next() {
close(e.Value.(bidirectionalEdge).outChannel)
}
time.Sleep(time.Second)
synCode = <-n.synChannel
fmt.Println("*", n.localId, "- Step #", synCode, "Publishing results")
n.leaderId = result
if n.localId == n.leaderId {
fmt.Println("###", n.localId, "- I AM THE LEADER!")
} else {
fmt.Println("###", n.localId, "- Leader elected:", n.leaderId)
}
return
}
func main() {
n := 5
synChan := make(chan int, 1)
lis_nodes := list.New()
for i := 1; i <= n; i++ {
lis_nodes.PushBack(NewGraphNode(i, synChan))
}
for e1 := lis_nodes.Front(); e1 != nil; e1 = e1.Next() {
for e2 := lis_nodes.Front(); e2 != nil; e2 = e2.Next() {
if e1 != e2 {
c1_2 := make(chan Message, 1)
c2_1 := make(chan Message, 1)
e1.Value.(*GraphNode).addEdge(bidirectionalEdge{c2_1, c1_2, e1.Value.(*GraphNode).localId, e2.Value.(*GraphNode).localId})
e2.Value.(*GraphNode).addEdge(bidirectionalEdge{c1_2, c2_1, e2.Value.(*GraphNode).localId, e1.Value.(*GraphNode).localId})
}
}
}
wg.Add(n)
for e := lis_nodes.Front(); e != nil; e = e.Next() {
fmt.Println("### Starting Node", e.Value.(*GraphNode).localId)
go e.Value.(*GraphNode).Run(&wg)
}
for i := 1; i <= n; i++ {
synChan <- 1
}
for i := 1; i <= n; i++ {
synChan <- 2
}
for i := 1; i <= n; i++ {
synChan <- 3
}
for i := 1; i <= n; i++ {
synChan <- 4
}
for i := 1; i <= n; i++ {
synChan <- 5
}
wg.Wait()
}
更多关于Golang中main函数死锁错误的解决方法的实战教程也可以访问 https://www.itying.com/category-94-b0.html
4 回复
Thomas_Bernier:
我的代码有问题
什么问题?你收到错误信息了吗?是哪个错误?你期望的结果是什么?你实际得到的结果又是什么?
更多关于Golang中main函数死锁错误的解决方法的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
好的,我明白了。
好的,我理解我的问题了。
当我没有第5步时,我在主函数中向我的 synChan 发送了5。
当我删除这段代码后:
for i := 1; i <= n; i++ {
synChan <- 5
}
它就能正常工作了。感谢您花时间阅读。
你好 lutzhorn,
我认为我得到了正确的输出,但最后出现了这个错误:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox157641356/prog.go:170 +0x720
最后,我需要返回领导者的标识,这是我的结果:
* 1 - Step # 4 Publishing results
### 1 - Leader elected: 5
* 5 - Step # 4 Publishing results
### 5 - I AM THE LEADER!
* 3 - Step # 4 Publishing results
### 3 - Leader elected: 5
* 2 - Step # 4 Publishing results
### 2 - Leader elected: 5
* 4 - Step # 4 Publishing results
### 4 - Leader elected: 5
你的代码出现了死锁问题,主要原因是通道同步逻辑和goroutine生命周期管理不当。以下是具体问题和解决方案:
问题分析
synChan通道容量不足:你创建了容量为1的缓冲通道,但需要向n个节点发送同步信号,这会导致阻塞listen函数中的通道读取:for receivedMessage := range edge.inChannel会一直阻塞等待数据,直到通道关闭- 通道关闭时机不当:主goroutine在等待所有节点完成,但节点在等待通道数据
修改后的代码
package main
import (
"container/list"
"fmt"
"sync"
"time"
)
func Max(x, y int) int {
if x < y {
return y
}
return x
}
type Message struct {
Id int
IsLeader bool
}
type GraphNode struct {
edges *list.List
localId int
leaderId int
synChannel <-chan int
}
type bidirectionalEdge struct {
inChannel <-chan Message
outChannel chan<- Message
fromId int
toId int
}
func NewGraphNode(id int, synChannel <-chan int) *GraphNode {
return &GraphNode{list.New(), id, -3, synChannel}
}
func (node *GraphNode) addEdge(edge bidirectionalEdge) {
node.edges.PushBack(edge)
}
func (edge bidirectionalEdge) listen(result *int, done chan<- struct{}) {
defer func() { done <- struct{}{} }()
fmt.Println("### Listening ", edge.fromId, ">", edge.toId)
last_max := -1
for receivedMessage := range edge.inChannel {
fmt.Println("<<<", edge.fromId, "- Received:", receivedMessage.Id, receivedMessage.IsLeader)
max := Max(edge.fromId, receivedMessage.Id)
if max > last_max {
last_max = max
select {
case edge.outChannel <- Message{max, false}:
fmt.Println(">>>", edge.fromId, "- Sent (", max, ") To", edge.toId)
case <-time.After(100 * time.Millisecond):
fmt.Println(">>>", edge.fromId, "- Timeout sending to", edge.toId)
}
}
}
if last_max > *result {
*result = last_max
}
}
func (n *GraphNode) Run(wg *sync.WaitGroup) {
defer wg.Done()
// 增加超时机制
timeout := time.After(5 * time.Second)
select {
case synCode := <-n.synChannel:
fmt.Println("*", n.localId, "- Step #", synCode, "Initialization")
for e := n.edges.Front(); e != nil; e = e.Next() {
select {
case e.Value.(bidirectionalEdge).outChannel <- Message{n.localId, false}:
fmt.Println(">>>", e.Value.(bidirectionalEdge).fromId, "- Sent (", n.localId, ") To", e.Value.(bidirectionalEdge).toId)
case <-timeout:
fmt.Println("*", n.localId, "- Timeout in initialization")
return
}
}
case <-timeout:
fmt.Println("*", n.localId, "- Timeout waiting for sync")
return
}
time.Sleep(50 * time.Millisecond)
select {
case synCode := <-n.synChannel:
fmt.Println("*", n.localId, "- Step #", synCode, "Listening")
result := -2
listenDone := make(chan struct{}, n.edges.Len())
for e := n.edges.Front(); e != nil; e = e.Next() {
go e.Value.(bidirectionalEdge).listen(&result, listenDone)
}
// 等待所有listen协程完成或超时
for i := 0; i < n.edges.Len(); i++ {
select {
case <-listenDone:
case <-time.After(500 * time.Millisecond):
fmt.Println("*", n.localId, "- Timeout waiting for listen")
}
}
time.Sleep(50 * time.Millisecond)
select {
case synCode := <-n.synChannel:
fmt.Println("*", n.localId, "- Step #", synCode, "Closing channels")
for e := n.edges.Front(); e != nil; e = e.Next() {
close(e.Value.(bidirectionalEdge).outChannel)
}
case <-timeout:
fmt.Println("*", n.localId, "- Timeout waiting for close signal")
return
}
time.Sleep(50 * time.Millisecond)
select {
case synCode := <-n.synChannel:
fmt.Println("*", n.localId, "- Step #", synCode, "Publishing results")
n.leaderId = result
if n.localId == n.leaderId {
fmt.Println("###", n.localId, "- I AM THE LEADER!")
} else {
fmt.Println("###", n.localId, "- Leader elected:", n.leaderId)
}
case <-timeout:
fmt.Println("*", n.localId, "- Timeout waiting for result signal")
return
}
case <-timeout:
fmt.Println("*", n.localId, "- Timeout in listening phase")
return
}
}
func main() {
n := 5
// 增加通道容量以避免阻塞
synChan := make(chan int, n*5)
lis_nodes := list.New()
for i := 1; i <= n; i++ {
lis_nodes.PushBack(NewGraphNode(i, synChan))
}
for e1 := lis_nodes.Front(); e1 != nil; e1 = e1.Next() {
for e2 := lis_nodes.Front(); e2 != nil; e2 = e2.Next() {
if e1 != e2 {
c1_2 := make(chan Message, n)
c2_1 := make(chan Message, n)
e1.Value.(*GraphNode).addEdge(bidirectionalEdge{c2_1, c1_2, e1.Value.(*GraphNode).localId, e2.Value.(*GraphNode).localId})
e2.Value.(*GraphNode).addEdge(bidirectionalEdge{c1_2, c2_1, e2.Value.(*GraphNode).localId, e1.Value.(*GraphNode).localId})
}
}
}
var wg sync.WaitGroup
wg.Add(n)
for e := lis_nodes.Front(); e != nil; e = e.Next() {
fmt.Println("### Starting Node", e.Value.(*GraphNode).localId)
go e.Value.(*GraphNode).Run(&wg)
}
// 发送同步信号
for step := 1; step <= 4; step++ {
for i := 1; i <= n; i++ {
synChan <- step
}
time.Sleep(100 * time.Millisecond)
}
wg.Wait()
close(synChan)
fmt.Println("All nodes completed")
}
主要修改点:
- 增加通道容量:
synChan和消息通道都增加了缓冲容量 - 添加超时机制:在所有通道操作中添加超时处理
- 改进同步逻辑:使用
select语句配合超时避免永久阻塞 - 添加完成信号:
listen函数完成后发送信号 - 调整主函数同步:在同步信号发送之间添加短暂延迟
这些修改可以防止死锁,确保程序能够正常完成执行。

