Hang channels keep order when locked?

I have a chunk of channels that all receive the same message:

func broadcast(c <-chan string, chans []chan<- string) { for msg := range c { for _, ch := range chans { ch <- msg } } } 

However, since each of the channels in chans potentially read at a different speed, I do not want to block the other channels when I receive a slow consumer. I solved this with goroutines:

 func broadcast(c <-chan string, chans []chan<- string) { for msg := range c { for _, ch := range chans { go func() { ch <- msg }() } } } 

However, the order of the messages transmitted to each channel is important. I looked at the specification to see if the channels keep order when locked, and all I found was the following:

If the capacity is greater than zero, the channel is asynchronous: communication operations are successful without blocking, if the buffer is not full (sent) or empty (receives), and the elements are received in the order in which they are sent.

For me, if a record is locked, it is not sent, but is waiting for sending. In this assumption, the above does not say anything about the sending order, when several letters are blocked during recording.

Are there any guarantees regarding the sending procedure after unlocking the channel?

+7
source share
4 answers

No, there are no guarantees.

Even when the channel is not full, if two goroutines start at about the same time to send to it, I donโ€™t think there is any guarantee that the goroutine that was launched first is launched first. Therefore, you cannot count on incoming messages.

+6
source

You can delete a message if the channel is full (and then set a flag to pause the client and send them a message that they are discarding messages or something else).

Something line by line (unchecked):

 type Client struct { Name string ch chan<-string } func broadcast(c <-chan string, chans []*Client) { for msg := range c { for _, ch := range chans { select { case ch.ch <- msg: // all okay default: log.Printf("Channel was full sending '%s' to client %s", msg, ch.Name) } } } } 
+4
source

There are no warranties in this code.

The main problem with this code example is not the channel behavior, but the many goroutines created. All goroutines "quit" within the same integral cycle without additional synchronization, so even before they start sending messages, we just donโ€™t know which ones will be executed in the first place.

However, this raises a legitimate question as a whole: if we somehow guarantee the order of several instructions for sending a lock, do we guarantee to receive them in the same order?

The "incident" property is difficult to create before sending. I am afraid this is not possible because:

  • Anything can happen before sending the command: for example, other goroutines execute their own messages or not.
  • Gorotin, blocked during sending, cannot simultaneously control other types of synchronization.

For example, if I have 10 goroutines with numbers from 1 to 10, I have no way to allow them to send their number to the channel at the same time, in the correct order. All I can do is use different kinds of sequential tricks, for example, do sorting in one single goroutine.

+1
source

This is a complement to already posted answers.

As almost everyone has stated that the problem is the execution order of the larynx, you can easily copy the execution of goroutine using channels by going around the number of goroutine you want to run:

 func coordinated(coord chan int, num, max int, work func()) { for { n := <-coord if n == num { work() coord <- (n+1) % max } else { coord <- n } } } coord := make(chan int) go coordinated(coord, 0, 3, func() { println("0"); time.Sleep(1 * time.Second) }) go coordinated(coord, 1, 3, func() { println("1"); time.Sleep(1 * time.Second) }) go coordinated(coord, 2, 3, func() { println("2"); time.Sleep(1 * time.Second) }) coord <- 0 

or with the help of a central Goroutine who performs workers in an orderly manner:

 func executor(funs chan func()) { for { worker := <-funs worker() funs <- worker } } funs := make(chan func(), 3) funs <- func() { println("0"); time.Sleep(1 * time.Second) } funs <- func() { println("1"); time.Sleep(1 * time.Second) } funs <- func() { println("2"); time.Sleep(1 * time.Second) } go executor(funs) 

These methods, of course, remove all parallelism due to synchronization. However, the parallel aspect of your program remains.

0
source