ZeroHour
ZeroHour

Reputation: 351

Proper way to gain access to a channel length in Go

I have been using Go for a little and still getting better everyday, but not an expert per se. Currently I am tackling concurrency and goroutines as I think that is the final unknown in my Go toolbelt. I think I am getting the hang of it as such, but still definitely a beginner.

The task I am having an issue with seems pretty basic to me, but nothing I have tried works. I would like to figure out a way to calculate the length of a channel.

From what I have gathered, len() only works on buffered channels so that won't help me in this case. What I am doing is reading values from the DB in batches. I have a generator func that goes like

func gen() chan Result {
  out := make(chan Result)

  go func() {
    ... query db
    for rows.Next() {
      out <- row
    }
     close(out)
   }()

  return out
}

then I am using it as such

c := gen()

...

// do other stuff

I would either like to return the count with the out channel, or wrap all of it in a struct type and just return that.

like so:

c, len := gen()

or:

a := gen()

fmt.Println(a.c)
fmt.Println(a.len)

I believe I have tried all but using atomic, which I think would actually work but I read around and it apparently isn't the right thing to use atomic for. What other options do I have that either don't leave me with a 0 or blocks infinitely

Thanks!

Upvotes: 2

Views: 4568

Answers (3)

DBZ7
DBZ7

Reputation: 57

You are using a not buffered channels. Thank you for thatπŸ‘πŸ‘πŸ‘ŒπŸ™Œ Unbuffered channel uses no memory. thus never contains nothing ! The only purpose of unbuffered channels are for achieving synchronicity between goroutine by passing an element from one to another. That's it !

    go func(){
        c:=make(chan struct{})
        c<-struct{}{} // Definitely locked
    }()

another deadlock

    go func(){
        c:=make(chan struct{})
        <-c // Definitely locked
        c<-struct{}{} // Never get there
    }()

use another goroutine to read the channel

    go func(){
        c:=make(chan struct{})
        go func(){<-c}()
        c<-struct{}{}
    }()

In your case you have a generator, which means you have to read the channel until the producer goroutine will close it. It is a good design that ensures that your goroutine are not dangling.

// Read the channel until the injecter goroutine finishes and closes it.
for r := range gen() {
...
}
// goroutine inner of gen() as finished

Upvotes: 1

James Antill
James Antill

Reputation: 2855

I am assuming, from your follow on answers, you actually want to know "good" values for the workers pool and the buffer on the channel to keep everything working "optimally".

This is extremely hard, and depends on what the workers are doing, but at a first guess I'd look at a minimal value of buffered channel and a pool of workers at runtime.GOMAXPROCS(0). If you have a lot of resources then you could go as far as "infinite" workers.

Upvotes: 0

Jonathan Hall
Jonathan Hall

Reputation: 79604

The len built-in will return the "length" of a channel:

func len(v Type) int

The len built-in function returns the length of v, according to its type:

Array: the number of elements in v.
Pointer to array: the number of elements in *v (even if v is nil).
Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
String: the number of bytes in v.
Channel: the number of elements queued (unread) in the channel buffer;

if v is nil, len(v) is zero.

But I don't think that will help you.

What you really need is a new approach to your problem: counting the items in queue in a channel is not an appropriate way to handle "batches" of tasks.

What do you need this length for?

Upvotes: 2

Related Questions