FAQ
Hello,

According to Effective Go:
A buffered channel can be used like a semaphore, for instance to limit
throughput.
In this example, incoming requests are passed to handle, which sends a
value into the
channel, processes the request, and then receives a value from the channel
to ready
the “semaphore” for the next consumer. The capacity of the channel buffer
limits
the number of simultaneous calls to process.
So it makes sense to me to buffer a channel for sending when we need more
throughput and responsiveness.

Then is there any case that receiving from a buffered channel can be useful?


Here's the full code to show what happens with buffered channel receiving.

http://play.golang.org/p/aouQ_RpKyr

package main

import "log"

func main() {
slice := []float64{23.0, 23, 23, -123.2323, 23, 123.23, -2.93, 23.1,
-101.34, 17.34}
sum := 0.0
for _, elem := range slice {
sum += elem
}

counter1 := NewChannelCounter(0)
defer counter1.Close()

for _, elem := range slice {
counter1.Add(elem)
}
val1 := counter1.Get()
if val1 != sum {
log.Fatalf("NewChannelCounter No Buffer wrong. Expected %v but got %v\n",
sum, val1)
}

counter2 := NewChannelCounter(10)
defer counter2.Close()

for _, elem := range slice {
counter2.Add(elem)
}
val2 := counter2.Get()
if val2 != sum {
log.Fatalf("NewChannelCounter No Buffer wrong. Expected %v but got %v\n",
sum, val2)
}
// 2015/08/07 17:49:25 NewChannelCounter No Buffer wrong. Expected
28.167699999999993 but got 23
}

// Counter is an interface for counting.
// It contains counting data as long as a type
// implements all the methods in the interface.
type Counter interface {
// Get returns the current count.
Get() float64

// Add adds the delta value to the counter.
Add(delta float64)
}

// ChannelCounter counts through channels.
type ChannelCounter struct {
valueChan chan float64
deltaChan chan float64
done chan struct{}
}

func NewChannelCounter(buf int) *ChannelCounter {
c := &ChannelCounter{
make(chan float64, buf),
make(chan float64, buf),
make(chan struct{}),
}
go c.Run()
return c
}

func (c *ChannelCounter) Run() {

var value float64

for {

// "select" statement chooses which of a set of
// possible send or receive operations will proceed.
select {

case delta := <-c.deltaChan:
value += delta

case <-c.done:
return

case c.valueChan <- value:
// Do nothing.

// If there is no default case, the "select" statement
// blocks until at least one of the communications can proceed.
default:
// default clauses in the select statements
// execute when no other case is ready
continue
}

}
}

func (c *ChannelCounter) Get() float64 {
return <-c.valueChan
}

func (c *ChannelCounter) Add(delta float64) {
c.deltaChan <- delta
}

func (c *ChannelCounter) Close() {
c.done <- struct{}{}
close(c.deltaChan)
}



When I buffered the receiver, it gets non-deterministic, because receiving
from the buffered channel
is non-blocking, and I can't think of any way to make this work correctly.
Or am I doing something wrong?


Thanks!

--
You received this message because you are subscribed to the Google Groups "golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Search Discussions

  • Gyu-Ho Lee at Aug 8, 2015 at 1:06 am
    Sorry for the typos. The example only crashes with buffered channel. The
    counter2.
    On Aug 7, 2015 6:03 PM, "Gyu-Ho Lee" wrote:

    Hello,

    According to Effective Go:
    A buffered channel can be used like a semaphore, for instance to limit
    throughput.
    In this example, incoming requests are passed to handle, which sends a
    value into the
    channel, processes the request, and then receives a value from the
    channel to ready
    the “semaphore” for the next consumer. The capacity of the channel buffer
    limits
    the number of simultaneous calls to process.
    So it makes sense to me to buffer a channel for sending when we need more
    throughput and responsiveness.

    Then is there any case that receiving from a buffered channel can be
    useful?


    Here's the full code to show what happens with buffered channel receiving.

    http://play.golang.org/p/aouQ_RpKyr

    package main

    import "log"

    func main() {
    slice := []float64{23.0, 23, 23, -123.2323, 23, 123.23, -2.93, 23.1,
    -101.34, 17.34}
    sum := 0.0
    for _, elem := range slice {
    sum += elem
    }

    counter1 := NewChannelCounter(0)
    defer counter1.Close()

    for _, elem := range slice {
    counter1.Add(elem)
    }
    val1 := counter1.Get()
    if val1 != sum {
    log.Fatalf("NewChannelCounter No Buffer wrong. Expected %v but got %v\n",
    sum, val1)
    }

    counter2 := NewChannelCounter(10)
    defer counter2.Close()

    for _, elem := range slice {
    counter2.Add(elem)
    }
    val2 := counter2.Get()
    if val2 != sum {
    log.Fatalf("NewChannelCounter No Buffer wrong. Expected %v but got %v\n",
    sum, val2)
    }
    // 2015/08/07 17:49:25 NewChannelCounter No Buffer wrong. Expected
    28.167699999999993 but got 23
    }

    // Counter is an interface for counting.
    // It contains counting data as long as a type
    // implements all the methods in the interface.
    type Counter interface {
    // Get returns the current count.
    Get() float64

    // Add adds the delta value to the counter.
    Add(delta float64)
    }

    // ChannelCounter counts through channels.
    type ChannelCounter struct {
    valueChan chan float64
    deltaChan chan float64
    done chan struct{}
    }

    func NewChannelCounter(buf int) *ChannelCounter {
    c := &ChannelCounter{
    make(chan float64, buf),
    make(chan float64, buf),
    make(chan struct{}),
    }
    go c.Run()
    return c
    }

    func (c *ChannelCounter) Run() {

    var value float64

    for {

    // "select" statement chooses which of a set of
    // possible send or receive operations will proceed.
    select {

    case delta := <-c.deltaChan:
    value += delta

    case <-c.done:
    return

    case c.valueChan <- value:
    // Do nothing.

    // If there is no default case, the "select" statement
    // blocks until at least one of the communications can proceed.
    default:
    // default clauses in the select statements
    // execute when no other case is ready
    continue
    }

    }
    }

    func (c *ChannelCounter) Get() float64 {
    return <-c.valueChan
    }

    func (c *ChannelCounter) Add(delta float64) {
    c.deltaChan <- delta
    }

    func (c *ChannelCounter) Close() {
    c.done <- struct{}{}
    close(c.deltaChan)
    }



    When I buffered the receiver, it gets non-deterministic, because receiving
    from the buffered channel
    is non-blocking, and I can't think of any way to make this work correctly.
    Or am I doing something wrong?


    Thanks!

    --
    You received this message because you are subscribed to a topic in the
    Google Groups "golang-nuts" group.
    To unsubscribe from this topic, visit
    https://groups.google.com/d/topic/golang-nuts/GG6PfdvjfBA/unsubscribe.
    To unsubscribe from this group and all its topics, send an email to
    golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Dustin at Aug 8, 2015 at 1:16 am
    Couple quick points:

    * lose the default case in your for loop. You want that select statment to block until one of the channels is ready, otherwise you have a busy loop. Your playground code actually returns "process took too long" because of that.

    * remove the close(deltaChan) from the counter close, if you have a concurrent call to Add() with close it will cause a panic.

    * for add & get value, use a select statement and include done, otherwise they will block indef. if they are called after Close(). I would call close(done) instead of sending on done so that it broadcasts as well.

    And yes, if you want Get() to include your call to Add() after Add returns, then it would have to be an unbuffered channel.

    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Roberto Zanotto at Aug 8, 2015 at 1:51 am
    The question sounds weird to me...

    It's useful receiving from a buffered channel every time it's useful
    sending to a buffered channel, it's the same channel for sending and
    receiving. It may or may not be useful to make that communication
    asynchronous/buffered. There is no such thing as buffered channel being
    useful for sending but not for receiving. The buffer is a property of the
    channel/communication, not of the sender or receiver :)
    On Saturday, August 8, 2015 at 3:03:28 AM UTC+2, Gyu-Ho Lee wrote:

    So it makes sense to me to buffer a channel for sending when we need more
    throughput and responsiveness.
    Then is there any case that receiving from a buffered channel can be
    useful?
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Gyu-Ho Lee at Aug 8, 2015 at 3:13 am
    Thanks Dustin. Yeah, I missed that part. I don't really need close here.


    On Friday, August 7, 2015 at 6:16:54 PM UTC-7, Dustin wrote:

    Couple quick points:

    * lose the default case in your for loop. You want that select statment to
    block until one of the channels is ready, otherwise you have a busy loop.
    Your playground code actually returns "process took too long" because of
    that.

    * remove the close(deltaChan) from the counter close, if you have a
    concurrent call to Add() with close it will cause a panic.

    * for add & get value, use a select statement and include done, otherwise
    they will block indef. if they are called after Close(). I would call
    close(done) instead of sending on done so that it broadcasts as well.

    And yes, if you want Get() to include your call to Add() after Add
    returns, then it would have to be an unbuffered channel.
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.
  • Gyu-Ho Lee at Aug 8, 2015 at 3:18 am
    I think I was confused by this example that buffered channel appears to
    behave non-deterministically.
    http://play.golang.org/p/F6u3yMGFFY

    But you are right. It's not the matter of receiving and sending. It's just
    the way buffered channel works.

    Thanks!
    On Friday, August 7, 2015 at 6:51:20 PM UTC-7, Roberto Zanotto wrote:

    The question sounds weird to me...

    It's useful receiving from a buffered channel every time it's useful
    sending to a buffered channel, it's the same channel for sending and
    receiving. It may or may not be useful to make that communication
    asynchronous/buffered. There is no such thing as buffered channel being
    useful for sending but not for receiving. The buffer is a property of the
    channel/communication, not of the sender or receiver :)
    On Saturday, August 8, 2015 at 3:03:28 AM UTC+2, Gyu-Ho Lee wrote:

    So it makes sense to me to buffer a channel for sending when we need more
    throughput and responsiveness.
    Then is there any case that receiving from a buffered channel can be
    useful?
    --
    You received this message because you are subscribed to the Google Groups "golang-nuts" group.
    To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscribe@googlegroups.com.
    For more options, visit https://groups.google.com/d/optout.

Related Discussions

Discussion Navigation
viewthread | post
Discussion Overview
groupgolang-nuts @
categoriesgo
postedAug 8, '15 at 1:03a
activeAug 8, '15 at 3:18a
posts6
users3
websitegolang.org

People

Translate

site design / logo © 2021 Grokbase