Reputation: 31
The concept seems simple to explain, but a tad harder to implement ("correctly").
The tl;dr is I want to run multiple functions that push output into a single channel.
As a sample working test (with multiple channels), to elaborate on my question https://play.golang.org/p/1ztCvPFLXKv
package main
import (
"fmt"
"time"
)
type intTest struct {
ID int
Number int
}
func modify1(channelID string, res chan []intTest) {
s := []intTest{}
for i := 0; i < 10; i++ {
fmt.Printf("Adding inside: %s\n", channelID)
s = append(s, intTest{i, 0})
time.Sleep(100 * time.Millisecond)
}
res <- s
}
func modify2(channelID string, res chan []intTest) {
s := []intTest{}
for i := 10; i < 20; i++ {
fmt.Printf("Adding inside: %s\n", channelID)
s = append(s, intTest{i, 0})
time.Sleep(200 * time.Millisecond)
}
res <- s
}
func modify3(channelID string, res chan []intTest) {
s := []intTest{}
for i := 20; i < 30; i++ {
fmt.Printf("Adding inside: %s\n", channelID)
s = append(s, intTest{i, 0})
time.Sleep(300 * time.Millisecond)
}
res <- s
}
func main() {
channelA := make(chan []intTest)
channelB := make(chan []intTest)
channelC := make(chan []intTest)
go modify1("A", channelA)
go modify2("B", channelB)
go modify3("C", channelC)
b := append(<-channelA, <-channelB...)
b = append(b, <-channelC...)
fmt.Println(b)
}
Output:
Adding inside: C
Adding inside: A
Adding inside: B
..snip..
Adding inside: C
Adding inside: C
Adding inside: C
[{0 0} {1 0} {2 0} {3 0} {4 0} {5 0} {6 0} {7 0} {8 0} {9 0} {10 0} {11 0} {12 0} {13 0} {14 0} {15 0} {16 0} {17 0} {18 0} {19 0} {20 0} {21 0} {22 0} {23 0} {24 0} {25 0} {26 0} {27 0} {28 0} {29 0}]
However, I would like to achieve something like this: https://play.golang.org/p/qvC88LwkanY Output:
Adding inside: C
Adding inside: A
Adding inside: B
..snip
Adding inside: B
Adding inside: A
Adding inside: C
[{0 0} {1 0} {2 0} {3 0} {4 0} {5 0} {6 0} {7 0} {8 0} {9 0}]
but as shown the functions modify2 & modify3 visually seems like they never get added.
Is this possible, or is the top sample more feasible?
Upvotes: 3
Views: 4115
Reputation: 4431
I would like to achieve something like this
channelA := make(chan []intTest)
go modify1("A", channelA)
go modify2("B", channelA)
go modify3("C", channelA)
Is this possible, or is the top sample more feasible?
Yes: you can use a single channel across multiple goroutines -- that's what channels are designed for.
but as shown the functions modify2 & modify3 visually seems like they never get added.
The problem you have there is that you only called the receive operator once:
b := append(<-channelA)
fmt.Println(b)
The other two goroutines were either blocked waiting to send their result, or still building their result, when your main
goroutine exited.
If you change your main()
function to this, you can see that all three workers will send their results over the channel if another routine is ready to receive (because you used an unbuffered channel, sends will block until a receiver is ready):
func main() {
ch := make(chan []intTest)
go modify1("A", ch)
go modify2("B", ch)
go modify3("C", ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
fmt.Println(<-ch)
}
Which outputs:
Adding inside: C
Adding inside: A
Adding inside: B
Adding inside: A
Adding inside: A
Adding inside: B
Adding inside: C
Adding inside: A
Adding inside: B
Adding inside: A
Adding inside: A
Adding inside: B
Adding inside: C
Adding inside: A
Adding inside: A
Adding inside: B
Adding inside: A
Adding inside: C
Adding inside: A
Adding inside: B
[{0 0} {1 0} {2 0} {3 0} {4 0} {5 0} {6 0} {7 0} {8 0} {9 0}]
Adding inside: C
Adding inside: B
Adding inside: B
Adding inside: C
Adding inside: B
Adding inside: C
Adding inside: B
[{10 0} {11 0} {12 0} {13 0} {14 0} {15 0} {16 0} {17 0} {18 0} {19 0}]
Adding inside: C
Adding inside: C
Adding inside: C
[{20 0} {21 0} {22 0} {23 0} {24 0} {25 0} {26 0} {27 0} {28 0} {29 0}]
You can then change that code to append received elements to a single list prior to output, or otherwise format the received elements in whatever way you like.
I thought it's almost like popping onto a stack and then pulling the whole stack
With an initialized, not-closed, buffered channel, a send statement puts one element onto a queue, and a receive operation pops one element off the queue and returns it. It's first in first out (FIFO) order, so it's a queue rather than a stack. In the examples above the channel is unbuffered, so a send has to wait for a goroutine ready to receive, and a receive has to wait for a goroutine ready to send. Main is a goroutine as well.
Upvotes: 2