简体   繁体   中英

Timeout and Iterating Over Unbuffered Channel in Go

In my code I have a connected graph of nodes, (all nodes are connected to each other directly or indirectly). Upon initialisation each node has a Value which is a random 64 bit integer.

type Node struct {
    ID *uuid.UUID

    Value int64

    ListenChannel chan Message

    Connections map[uuid.UUID]*Node
}


type Message struct {
    Value int64
}

The idea is that they will communicate with each other and come to consensus about what is the largest Value . (Communication happens over the ListenChannel that each node has, also nodes are modeled as goroutines)

func (node *Node) FormConsensus(wg *sync.WaitGroup, retChannel chan int64) {
    defer wg.Done()
    defer func() {
        // this will be a non-blocking operation since the channel is buffered
        retChannel <- node.Value
    }()

    listenWg := sync.WaitGroup{}

    listenWg.Add(1)
    // Add logic to pass messages to and fro and find the maximum value from the network
    go func() {
        for timedOut := false; !timedOut; {
            select {
            case receivedMessage := <-node.ListenChannel:
                if receivedMessage.Value > node.Value {
                    fmt.Println("Received value")
                    node.Value = receivedMessage.Value
                    fmt.Println("Received large value")
                    node.BroadcastValue()
                }
            case <-time.After(5 * time.Second):
                fmt.Println("Timeout")
                listenWg.Done()
                close(node.ListenChannel)
                timedOut = true
            }
        }
    }()

    node.BroadcastValue()
    listenWg.Wait()
}

func (node *Node) BroadcastValue() {
    fmt.Println("broadcasting", node.Value)
    for _, connection := range node.Connections {
        connection.ListenChannel <- Message{node.Value}
    }
}

To achieve this, every node first broadcasts its value to its connections, and also has a goroutine which continuously listens on its own listenChannel for values that other nodes are sending. If the received values are larger than its own, then it updates its own value, and broadcasts the updated value.

With the timeout logic inside the switch case, I am trying to set a 5 second timout, ie, if no messages are received on the channel for 5 seconds, then assume consesus has been reached and exit.

However on executing the FormConsensus function, it never exits. I am not sure where I am going wrong, and as I am very new to Go, I might be doing things which are completely unidiomatic. Other than understanding what is going wrong, it would be also very helpful to understand if there is better way to achieve what I am trying to in Go.

I'm pretty sure the error is here.

func (node *Node) BroadcastValue() {
fmt.Println("broadcasting", node.Value)
for _, connection := range node.Connections {
    select {
    case connection.ListenChannel <- Message{node.Value}:
        fmt.Println("--test message written")
    default:
        fmt.Println("--test message dropped")
    }
}

}

It looks like the channel write operation is blocking because the buffer is full. You will have to investigate why it is sending more messages than you expect.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM