return early from range of a channel











up vote
1
down vote

favorite












Is it possible to implement something akin to yield from python in go? That is, I want a producer/consumer paradigm where the consumer can ignore values produced by the producer at any time without manifesting a memory leak.



In python I could have a producer that produces infinite values and the client can consume as many as they wish.



def producer():
i = 0
while True:
yield i
i += 1

def consumer():
for i in producer():
if i == 2:
return


The common advice for this is for the producer to return a channel that the client uses in a range expression, and the producer can put its values into the channel. This is fine as long as the client reads all the values in the channel, but if the client breaks control from the range loop early the producer channel will be left hanging forever using up memory.



package main

func yield() chan int {
out := make(chan int)
go func(){
out <- 1
out <- 2
close(out)
}()
return out
}

func test(){
for _ = range yield() {
// return
}
}

func main(){
for {
test()
}
}


If the return in test is uncommented this code will use memory without bound, because the channel returned by yield has a value that is waiting to be read and apparently neither the channel nor the goroutine can be garbage collected safely.



Some possible solutions (neither of which I like) are





  1. Change out in yield to be a buffered channel with exactly the amount of space as values that will be produced. In that case yield could be



    func yield() chan int {
    out := make(chan int, 2)
    out <- 1
    out <- 2
    close(out)
    return out
    }




But this is suboptimal because I don't know in general how many items yield will produce. This style is similar to just returning an array.




  1. Have the yield method use a timeout for putting values into the channel, and if the timeout is reached then close the channel early. This makes assumptions about the execution time of the client, which seems like a bad idea.










share|improve this question






















  • Seems this was discussed at github.com/golang/go/issues/19702, and likely will not be fixed.
    – jonr
    Nov 21 at 0:32










  • You can implement an iterator design pattern: it wouldn't be as elegant as yield, but semantically the same play.golang.org/p/7zw6iIwcDxH
    – zerkms
    Nov 21 at 1:14












  • See Stopping Short in Go Concurrency Patterns: Pipelines and cancellation.
    – ThunderCat
    Nov 21 at 1:39















up vote
1
down vote

favorite












Is it possible to implement something akin to yield from python in go? That is, I want a producer/consumer paradigm where the consumer can ignore values produced by the producer at any time without manifesting a memory leak.



In python I could have a producer that produces infinite values and the client can consume as many as they wish.



def producer():
i = 0
while True:
yield i
i += 1

def consumer():
for i in producer():
if i == 2:
return


The common advice for this is for the producer to return a channel that the client uses in a range expression, and the producer can put its values into the channel. This is fine as long as the client reads all the values in the channel, but if the client breaks control from the range loop early the producer channel will be left hanging forever using up memory.



package main

func yield() chan int {
out := make(chan int)
go func(){
out <- 1
out <- 2
close(out)
}()
return out
}

func test(){
for _ = range yield() {
// return
}
}

func main(){
for {
test()
}
}


If the return in test is uncommented this code will use memory without bound, because the channel returned by yield has a value that is waiting to be read and apparently neither the channel nor the goroutine can be garbage collected safely.



Some possible solutions (neither of which I like) are





  1. Change out in yield to be a buffered channel with exactly the amount of space as values that will be produced. In that case yield could be



    func yield() chan int {
    out := make(chan int, 2)
    out <- 1
    out <- 2
    close(out)
    return out
    }




But this is suboptimal because I don't know in general how many items yield will produce. This style is similar to just returning an array.




  1. Have the yield method use a timeout for putting values into the channel, and if the timeout is reached then close the channel early. This makes assumptions about the execution time of the client, which seems like a bad idea.










share|improve this question






















  • Seems this was discussed at github.com/golang/go/issues/19702, and likely will not be fixed.
    – jonr
    Nov 21 at 0:32










  • You can implement an iterator design pattern: it wouldn't be as elegant as yield, but semantically the same play.golang.org/p/7zw6iIwcDxH
    – zerkms
    Nov 21 at 1:14












  • See Stopping Short in Go Concurrency Patterns: Pipelines and cancellation.
    – ThunderCat
    Nov 21 at 1:39













up vote
1
down vote

favorite









up vote
1
down vote

favorite











Is it possible to implement something akin to yield from python in go? That is, I want a producer/consumer paradigm where the consumer can ignore values produced by the producer at any time without manifesting a memory leak.



In python I could have a producer that produces infinite values and the client can consume as many as they wish.



def producer():
i = 0
while True:
yield i
i += 1

def consumer():
for i in producer():
if i == 2:
return


The common advice for this is for the producer to return a channel that the client uses in a range expression, and the producer can put its values into the channel. This is fine as long as the client reads all the values in the channel, but if the client breaks control from the range loop early the producer channel will be left hanging forever using up memory.



package main

func yield() chan int {
out := make(chan int)
go func(){
out <- 1
out <- 2
close(out)
}()
return out
}

func test(){
for _ = range yield() {
// return
}
}

func main(){
for {
test()
}
}


If the return in test is uncommented this code will use memory without bound, because the channel returned by yield has a value that is waiting to be read and apparently neither the channel nor the goroutine can be garbage collected safely.



Some possible solutions (neither of which I like) are





  1. Change out in yield to be a buffered channel with exactly the amount of space as values that will be produced. In that case yield could be



    func yield() chan int {
    out := make(chan int, 2)
    out <- 1
    out <- 2
    close(out)
    return out
    }




But this is suboptimal because I don't know in general how many items yield will produce. This style is similar to just returning an array.




  1. Have the yield method use a timeout for putting values into the channel, and if the timeout is reached then close the channel early. This makes assumptions about the execution time of the client, which seems like a bad idea.










share|improve this question













Is it possible to implement something akin to yield from python in go? That is, I want a producer/consumer paradigm where the consumer can ignore values produced by the producer at any time without manifesting a memory leak.



In python I could have a producer that produces infinite values and the client can consume as many as they wish.



def producer():
i = 0
while True:
yield i
i += 1

def consumer():
for i in producer():
if i == 2:
return


The common advice for this is for the producer to return a channel that the client uses in a range expression, and the producer can put its values into the channel. This is fine as long as the client reads all the values in the channel, but if the client breaks control from the range loop early the producer channel will be left hanging forever using up memory.



package main

func yield() chan int {
out := make(chan int)
go func(){
out <- 1
out <- 2
close(out)
}()
return out
}

func test(){
for _ = range yield() {
// return
}
}

func main(){
for {
test()
}
}


If the return in test is uncommented this code will use memory without bound, because the channel returned by yield has a value that is waiting to be read and apparently neither the channel nor the goroutine can be garbage collected safely.



Some possible solutions (neither of which I like) are





  1. Change out in yield to be a buffered channel with exactly the amount of space as values that will be produced. In that case yield could be



    func yield() chan int {
    out := make(chan int, 2)
    out <- 1
    out <- 2
    close(out)
    return out
    }




But this is suboptimal because I don't know in general how many items yield will produce. This style is similar to just returning an array.




  1. Have the yield method use a timeout for putting values into the channel, and if the timeout is reached then close the channel early. This makes assumptions about the execution time of the client, which seems like a bad idea.







go yield






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 21 at 0:22









jonr

7051519




7051519












  • Seems this was discussed at github.com/golang/go/issues/19702, and likely will not be fixed.
    – jonr
    Nov 21 at 0:32










  • You can implement an iterator design pattern: it wouldn't be as elegant as yield, but semantically the same play.golang.org/p/7zw6iIwcDxH
    – zerkms
    Nov 21 at 1:14












  • See Stopping Short in Go Concurrency Patterns: Pipelines and cancellation.
    – ThunderCat
    Nov 21 at 1:39


















  • Seems this was discussed at github.com/golang/go/issues/19702, and likely will not be fixed.
    – jonr
    Nov 21 at 0:32










  • You can implement an iterator design pattern: it wouldn't be as elegant as yield, but semantically the same play.golang.org/p/7zw6iIwcDxH
    – zerkms
    Nov 21 at 1:14












  • See Stopping Short in Go Concurrency Patterns: Pipelines and cancellation.
    – ThunderCat
    Nov 21 at 1:39
















Seems this was discussed at github.com/golang/go/issues/19702, and likely will not be fixed.
– jonr
Nov 21 at 0:32




Seems this was discussed at github.com/golang/go/issues/19702, and likely will not be fixed.
– jonr
Nov 21 at 0:32












You can implement an iterator design pattern: it wouldn't be as elegant as yield, but semantically the same play.golang.org/p/7zw6iIwcDxH
– zerkms
Nov 21 at 1:14






You can implement an iterator design pattern: it wouldn't be as elegant as yield, but semantically the same play.golang.org/p/7zw6iIwcDxH
– zerkms
Nov 21 at 1:14














See Stopping Short in Go Concurrency Patterns: Pipelines and cancellation.
– ThunderCat
Nov 21 at 1:39




See Stopping Short in Go Concurrency Patterns: Pipelines and cancellation.
– ThunderCat
Nov 21 at 1:39

















active

oldest

votes











Your Answer






StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");

StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














 

draft saved


draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403593%2freturn-early-from-range-of-a-channel%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown






























active

oldest

votes













active

oldest

votes









active

oldest

votes






active

oldest

votes
















 

draft saved


draft discarded



















































 


draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53403593%2freturn-early-from-range-of-a-channel%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

Berounka

Sphinx de Gizeh

Different font size/position of beamer's navigation symbols template's content depending on regular/plain...