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生命周期管理不当。以下是具体问题和解决方案:

问题分析

  1. synChan 通道容量不足:你创建了容量为1的缓冲通道,但需要向n个节点发送同步信号,这会导致阻塞
  2. listen 函数中的通道读取for receivedMessage := range edge.inChannel 会一直阻塞等待数据,直到通道关闭
  3. 通道关闭时机不当:主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")
}

主要修改点:

  1. 增加通道容量synChan 和消息通道都增加了缓冲容量
  2. 添加超时机制:在所有通道操作中添加超时处理
  3. 改进同步逻辑:使用 select 语句配合超时避免永久阻塞
  4. 添加完成信号listen 函数完成后发送信号
  5. 调整主函数同步:在同步信号发送之间添加短暂延迟

这些修改可以防止死锁,确保程序能够正常完成执行。

回到顶部