-2

I currently have two functions pushNodes(node) and updateNodes(node). In the pushNodes function, I am pushing values through a channel that are to be used in updateNodes. In order to have accurate channel values saved, I need all pushNodes go routines to finish before starting updateNodes(). How can I still access the channel values after the GoRoutines have finished executing?

I continuously get "fatal error: all goroutines are asleep - deadlock!". Please let me know how I can get these values from the channel. Is there a better/alternate way to do this?

   //pushNodes is a function that will push the nodes values
   func pushNodes(node Node) {
      defer wg.Done()
      fmt.Printf("Pushing: %d \n", node.number)
      //Choose a random peer node
      var randomnode int = rand.Intn(totalnodes)
      for randomnode == node.number {
        rand.Seed(time.Now().UnixNano())
        randomnode = rand.Intn(totalnodes)
      }
      //If the current node is infected, send values through the channel
      if node.infected {
         sentchanneldata := ChannelData{infected: true, message: node.message}
         allnodes[randomnode].channel <- sentchanneldata
         fmt.Printf("Node %d sent a value of %t and %s to node %d!\n", node.number, sentchanneldata.infected, sentchanneldata.message, allnodes[randomnode].number)
    }


 //updateNodes is a function that will update the nodes values
  func updateNodes(node Node) {
    defer wg.Done()
    fmt.Printf("Updating: %d\n", node.number)
    //get value through node channel
    receivedchanneldata := <-node.channel
    fmt.Printf("Node %d received a value of %t and %s!\n", node.number, receivedchanneldata.infected, receivedchanneldata.message)
    // update value
    if receivedchanneldata.infected == true {
      node.infected = true
    }
    if receivedchanneldata.message != "" {
      node.message = receivedchanneldata.message
    }
    fmt.Printf("Update successful!\n")
  }

   //Part of main functions
    wg.Add(totalnodes)
    for node := range allnodes {
        go pushNodes(allnodes[node])
    }
    wg.Wait()
    fmt.Println("Infect function done!")

    wg.Add(totalnodes)
    for node := range allnodes {
        go updateNodes(allnodes[node])
    }
    wg.Wait()
mringes
  • 1
  • 1
  • 3
    You should make this a minimal, verifiable example we can actually compile and execute. I've got a lot of questions about where variables are being defined and such – erik258 Oct 01 '21 at 04:05
  • Please provide enough code so others can better understand or reproduce the problem. – Community Oct 08 '21 at 11:57

1 Answers1

1

How can I still access the channel values after the GoRoutines have finished executing?

A channel's existence, including any data that have been shoved into it, is independent of the goroutines that might read from or write to it, provided that at least one goroutine still exists that can read from and/or write to it. (Once all such goroutines are gone, the channel will—eventually—be GC'ed.)

Your code sample is unusable (as already noted) so we can't say precisely where you have gone wrong, but you'll get the kind of fatal message you report here:

fatal error: all goroutines are asleep - deadlock!

if you attempt to read from a channel in the last runnable goroutine, such that this goroutine goes to sleep to await a message on that channel, in such a way that the rest of the Go runtime can determine for certain that no currently-asleep goroutine will ever wake up and deliver a message on that channel. For instance, suppose you have 7 total goroutines running right as one of them reaches the following line of code:

msg = <-ch

where ch is an open channel with no data available right now. One of those 7 goroutines reaches this line and blocks ("goes to sleep"), waiting for one of the remaining six goroutines to do:

ch <- whatever

which would wake up that 7th goroutine. So now there are only 6 goroutines that can write on ch or close ch. If those six remaining goroutines also pass through the same line, one at a time or several or all at once, with none of them ever sending on the channel or closing it, those remaining goroutines will also block. When the last one of them blocks, the runtime will realize that the program is stuck, and panic.

If, however, only five of the remaining six goroutines block like this, and then the sixth one runs though a line reading:

close(ch)

that close operation will close the channel, causing all six of the "stuck asleep" goroutines to receive "end of data" represented by a zero-valued "fake" message msg. You can also use the two-valued form of receive:

msg, ok = <-ch

Here ok gets true if the channel isn't closed and msg contains a real message, but gets false if the channel is closed and msg now contains a zero-valued "fake" message.

Thus, you can either:

  • close the channel to indicate that you plan not to send anything else, or
  • carefully match up the number of "receive from channel" operations to the number of "send message on channel" operations.

The former is the norm with channels where there's no way to know in advance how many messages should be sent on the channel. It can still be used even if you do know. A typical construct for doing the close is:

ch := make(chan T)  // for some type T
// do any other setup that is appropriate

var wg sync.WaitGroup
wg.add(N)  // for some number N
// spin off some number of goroutines N, each of which may send
// any number of messages on the channel
for i := 0; i < N; i++ {
    go doSomething(&wg, ch)
    // in doSomething, call wg.Done() when done sending on ch
}

go func() {
    wg.Wait()  // wait for all N goroutines to finish
    close(ch)  // then, close the channel
}()

// Start function(s) that receive from the channel, either
// inline or in more goroutines here; have them finish when
// they see that the channel is closed.

This pattern relies on the ability to create an extra N+1'th goroutine—that's the anonymous function go func() { ... }() sequence—whose entire job in life is to wait for all the senders to say I am done sending. Each sender does that by calling wg.Done() once. That way, no sender has any special responsibility for closing the channel: they all just write and then announce "I'm done writing" when they are done writing. One goroutine has one special responsibility: it waits for all senders to have announced "I'm done writing", and then it closes the channel and exits, having finished its one job in life.

All receivers—whether that's one or many—now have an easy time knowing when nobody will ever send anything any more, because they see a closed channel at that point. So if most of the work is on the sending side, you can even use the main goroutine here with a simple for ... range ch loop.

torek
  • 448,244
  • 59
  • 642
  • 775