-1

How we can set something like listener on go channels that when someone has read something from the channel, that notify us?

Imagine we have a sequence number for channel entries and we wanna decrement it when someone had read a value from our channel somewhere out of our package.

meshkati
  • 1,720
  • 2
  • 16
  • 29
  • 4
    In general you can't get notified about another goroutine receiving from a channel, but you can structure your app so you don't need this. E.g. you can have a dedicated goroutine sending incrementing numbers on the channel, and so it is irrelevant if and how many values were received from it. – icza Apr 12 '19 at 10:10

3 Answers3

1

Unbuffered channels hand off data synchronously, so you already know when the data is read. Buffered channels work similarly when the buffer is full, but otherwise they don't block the same, so this approach wouldn't tell you quite the same thing. Depending on what your needs really are, consider also using tools like sync.WaitGroup.

ch = make(chan Data)
  ⋮
for {
      ⋮
    // make data available
    ch <- data

    // now you know it was read
    sequenceNumber--
      ⋮
}
Michael Urman
  • 15,737
  • 2
  • 28
  • 44
1

You could create a channel relay mechanism, to capture read events in realtime.

So for example:

func relayer(in <-chan MyStruct) <-chan MyStruct {
        out := make(chan MyStruct) // non-buffered chan (see below)

        go func() {     
                defer close(out)
                readCountLimit := 10

                for item := range in {
                        out <- item
                        // ^^^^ so this will block until some worker has read from 'out'
                        readCountLimit--
                }
        }()     
        return out      
}

Usage:

type MyStruct struct {
        // put your data fields here
}

ch := make(chan MyStruct) // <- original channel - used by producer to write to

rch := relayer(ch) // <- relay channel - used to read from

// consumers
go worker("worker 1", rch)
go worker("worker 2", rch)

// producer
for { ch <- MyStruct{} }
colm.anseo
  • 19,337
  • 4
  • 43
  • 52
0

You can do it in manual mode. implement some sort of ACK marker to the message. Something like this:

type Msg struct {
    Data int
    ack  bool
}

func (m *Msg) Ack() {
    m.ack = true
}

func (m *Msg) Acked() bool {
    return m.ack
}

func main() {
    ch := make(chan *Msg)
    msg := &Msg{Data: 1}
    go func() {
        for {
            if msg.Acked() {
                // do smth
            }
            time.Sleep(10 * time.Second)
        }
    }()
    ch <- msg

    for msg := range ch {
        msg.Ack()
    }
}

Code not tested. You can also add some additional information to Ack() method, say meta information about package and func, from where Ack() was called, this answer may be related: https://stackoverflow.com/a/35213181/3782382

asyndrige
  • 572
  • 6
  • 23