slice of channels and concurrent function execution - go

How to create slice of channels and run function double(i) concurrently inside slice iteration:
package main
import (
"fmt"
"time"
)
func double(i int) int {
result := 2 * i
fmt.Println(result)
time.Sleep(500000000)
return result
}
func notParallel(arr []int) (outArr []int) {
for _, i := range arr {
outArr = append(outArr, double(i))
}
return
}
// how to do the same as notParallel func in parallel way.
// For each element of array double func should evaluate concuruntly
// without waiting each next element to eval
func parallel(arr []int) (outArr []int) {
var chans []chan int
for i := 0; i < len(arr); i++ {
chans[i] = make(chan int) // i = 0 : panic: runtime error: index out of range
}
for counter, number := range arr {
go func() {
chans[counter] <- double(number)
}()
}
return
}
func main() {
arr := []int{7, 8, 9}
fmt.Printf("%d\n", notParallel(arr))
fmt.Printf("%d\n", parallel(arr))
}
playground
As function double(i) sleeps for 500 ms function notParallel(arr []int) works for 1500 ms for 3 elements of arr []int but function parallel(arr []int) would work about 500 ms.
In my implementation have error...
panic: runtime error: index out of range
... on line ...
chans[i] = make(chan int) // i = 0

In this case, you don't need to use chan.
package main
import (
"fmt"
"sync"
"time"
)
func double(i int) int {
result := 2 * i
fmt.Println(result)
time.Sleep(500000000)
return result
}
func notParallel(arr []int) (outArr []int) {
for _, i := range arr {
outArr = append(outArr, double(i))
}
return
}
// how to do the same as notParallel func in parallel way.
// For each element of array double func should evaluate concuruntly
// without waiting each next element to eval
func parallel(arr []int) (outArr []int) {
outArr = make([]int, len(arr))
var wg sync.WaitGroup
for counter, number := range arr {
wg.Add(1)
go func(counter int, number int) {
outArr[counter] = double(number)
wg.Done()
}(counter, number)
}
wg.Wait()
return
}
func main() {
arr := []int{7, 8, 9}
fmt.Printf("%d\n", notParallel(arr))
fmt.Printf("%d\n", parallel(arr))
}
Because parallel must wait all of finish of goroutine(s).
And I notice your code doesn't work because you refer counter, number in same function scope.

Related

Observer pattern or a Publish/Subscribe pattern for a cellular automaton

I am trying to code an observer pattern or a publish/submit pattern for a sort of cellular automaton.
The classical observer pattern does not to the trick because if a cell A subscribes to changes in a cell B and vice-versa, the application will run out of stack owing to the recursive approach (B.update() will call A.update() and so on and the app will run out of stack).
So I thought about using a publish/subscribe pattern where respective cells pass each other messages, rather than calling each other's update() methods.
Here is a simple example with two cells A and B:
package main
import (
"fmt"
ps "publish/pubsub"
)
func main() {
fmt.Printf("Starting\n")
chEnd := make(chan int)
// initialize
a := ps.NewNode(1, 0)
b := ps.NewNode(2, 0)
// connect nodes
a.Connect(b.ChOut)
b.Connect(a.ChOut)
// Start listening
a.Listen()
b.Listen()
// Start sending data on one arbitrary node
// to start the process.
a.ChIn <- 10
<-chEnd
}
and the corresponding lib
package pubsub
import (
"fmt"
)
type Node struct {
Id int
State int
ChOut chan int
ChIn chan int
}
func NewNode(id int, state int) Node {
chout := make(chan int)
var chin chan int
return Node{id, state, chout, chin}
}
func (p *Node) Broadcast(inItem int) {
p.ChOut <- inItem + 1
//time.Sleep(100 * time.Millisecond)
}
func (p *Node) Listen() {
go func() {
for {
select {
case inItem := <-p.ChIn:
fmt.Printf("%d: %d\n", p.Id, inItem)
p.Broadcast(inItem)
}
}
}()
}
func (p *Node) Connect(ch chan int) {
p.ChIn = ch
}
Each node has a input and an output channe.
The input channel of B is the output channel of A and vice-versa.
Every update consists merely of incrementing the data passed by the other cell.
It seems to work. So far, so good.
I tried to do something similar with a set of 4 cells A, B, C, D, in order to simulate a one dimensional cellular automaton of sorts.
In this second attempt,
each cell has two input channels (let and right) to listen to its closest left- and right-hand neighbour, respectively (ChinL and ChinR).
each cell has to output channels to communicate its latest updated state to its closest neighbours (ChoutL and ChoutR).
I must have done something wrong in the implementation of that scheme with 4 cells, because it yields odd results : the values passed back and forth between the 4 cells seem to hit a threshold instead of increasing at every consecutive step: here is the code:
package main
import (
"fmt"
ps "publish/pubsub"
)
func main() {
fmt.Printf("Starting\n")
chEnd := make(chan int)
// initialize
a := ps.NewNode(1, 0)
b := ps.NewNode(2, 0)
c := ps.NewNode(3, 0)
d := ps.NewNode(4, 0)
// connect nodes
a.ChInL = d.ChOutR
a.ChInR = b.ChOutL
b.ChInL = a.ChOutR
b.ChInR = c.ChOutL
c.ChInL = b.ChOutR
c.ChInR = d.ChOutL
d.ChInL = c.ChOutR
d.ChInR = a.ChOutL
// Start listening
go a.Listen()
go b.Listen()
go c.Listen()
go d.Listen()
go a.Broadcast()
go b.Broadcast()
go c.Broadcast()
go d.Broadcast()
// Start sending data on one arbitrary node
// to start the process.
a.ChInL <- 1
// Dummy read on channel to make main() wait
<-chEnd
}
/*
A B C D
LR LR LR LR
*/
and the corresponding lib
package pubsub
import (
"fmt"
"strings"
)
type Node struct {
Id int
State int
ChOutL chan int
ChOutR chan int
ChInL chan int
ChInR chan int
ChIO chan int
}
func NewNode(id int, state int) Node {
choutL := make(chan int)
choutR := make(chan int)
var chinL chan int
var chinR chan int
chIO := make(chan int)
return Node{id, state, choutL, choutR, chinL, chinR, chIO}
}
func (p *Node) Broadcast() {
for item := range p.ChIO {
p.ChOutL <- item + 1
p.ChOutR <- item + 1
fmt.Printf("%d: %d %s\n", p.Id, item, strings.Repeat("*", item))
}
}
func (p *Node) Listen() {
for {
//time.Sleep(100 * time.Millisecond)
select {
case inItem := <-p.ChInL:
go func() {
p.ChIO <- inItem
}()
case inItem := <-p.ChInR:
go func() {
p.ChIO <- inItem
}()
}
}
}
For completeness, here is the go.mod for the modules above:
module publish
go 1.17
For every signal a node receives on p.ChInL or p.ChInR you send out 2 signals. The first to p.ChOutL and the second to p.ChOutR. Since every node does this there will be an exponential amount of signals going around.
When running it locally the threshold is between 20 and 25. So about 2^25 33554432 signals going around. To see 26, the program will need to process 67108864 signals. So it will go past this threshold, just exponentially slower.
Now for the fix. I think you should implement some sort of tick system. So instead of sending update signals for every change to the automaton you send one update every tick like 20 times per second.
Maybe better yet, instead of using routines and channels, just make an slice of nodes and loop over them (again only updating the neighbor and not propagating it in the same tick/loop). Each iteration of the loop the state of all nodes has changed. I believe this is also how other automatons like game-of-live work. Now you can run the simulation as fast as your computer can run.
Here is one possible alternative:
package pubsub
import (
"fmt"
"math/rand"
"strings"
"time"
)
type Node struct {
Id int
State int
ChOutL chan int
ChOutR chan int
ChInL chan int
ChInR chan int
ChIO chan int
}
func NewNode(id int, state int) Node {
choutL := make(chan int)
choutR := make(chan int)
var chinL chan int
var chinR chan int
chIO := make(chan int)
return Node{id, state, choutL, choutR, chinL, chinR, chIO}
}
func (p *Node) Broadcast() {
for item := range p.ChIO {
rnd := rand.Intn(2)
if rnd == 0 {
p.ChOutL <- item + 1
} else {
p.ChOutR <- item + 1
}
fmt.Printf("%d: %d %s\n", p.Id, item, strings.Repeat("*", item))
}
}
func (p *Node) Listen() {
for {
time.Sleep(100 * time.Millisecond)
select {
case inItem := <-p.ChInL:
p.ChIO <- inItem
case inItem := <-p.ChInR:
p.ChIO <- inItem
}
}
}
Here is a second alternative:
package main
import (
"fmt"
ps "pub/pubsub"
)
func main() {
fmt.Printf("Hello\n")
chEnd := make(chan int)
A := ps.NewNode(1, 0)
B := ps.NewNode(2, 0)
C := ps.NewNode(3, 0)
D := ps.NewNode(4, 0)
B.LeftNode = &A
B.RightNode = &C
C.LeftNode = &B
C.RightNode = &D
D.LeftNode = &C
D.RightNode = &A
A.LeftNode = &D
A.RightNode = &B
A.Listen()
B.Listen()
C.Listen()
D.Listen()
A.State = 1
<-chEnd
}
//----
package pubsub
import (
"fmt"
"strings"
"sync"
"time"
)
type Node struct {
Id int
State int
LeftNode *Node
RightNode *Node
LeftState int
RightState int
}
var m sync.Mutex
func NewNode(id int, state int) Node {
return Node{id, state, nil, nil, 0, 0}
}
func (n *Node) Listen() {
go func() {
for {
m.Lock()
time.Sleep(10 * time.Millisecond)
if n.LeftState != n.LeftNode.State {
n.LeftState = n.LeftNode.State
n.State = n.LeftNode.State + 1
fmt.Printf("%d: %d %s\n", n.Id, n.State, strings.Repeat("*", n.State))
}
m.Unlock()
}
}()
go func() {
for {
m.Lock()
time.Sleep(10 * time.Millisecond)
if n.RightState != n.RightNode.State {
n.RightState = n.RightNode.State
n.State = n.RightNode.State + 1
fmt.Printf("%d: %d %s\n", n.Id, n.State, strings.Repeat("*", n.State))
}
m.Unlock()
}
}()
}

Passing a WaitGroup to a function changes behavior, why?

I have 3 merge sort implementations:
MergeSort: simple one without concurrency;
MergeSortSmart: with concurrency limited by buffered channel size limit. If buffer is full, calls the simple implementation;
MergeSortSmartBug: same strategy as the previous one, but with a small "refactor", passing wg pointer to a function reducing code duplication.
The first two works as expected, but the third one returns an empty slice instead of the sorted input. I couldn't understand what happened and found no answers as well.
Here is the playground link for the code: https://play.golang.org/p/DU1ypbanpVi
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
)
type pass struct{}
var semaphore = make(chan pass, runtime.NumCPU())
func main() {
rand.Seed(10)
s := make([]int, 16)
for i := 0; i < 16; i++ {
s[i] = int(rand.Int31n(1000))
}
fmt.Println(s)
fmt.Println(MergeSort(s))
fmt.Println(MergeSortSmart(s))
fmt.Println(MergeSortSmartBug(s))
}
func merge(l, r []int) []int {
tmp := make([]int, 0, len(l)+len(r))
for len(l) > 0 || len(r) > 0 {
if len(l) == 0 {
return append(tmp, r...)
}
if len(r) == 0 {
return append(tmp, l...)
}
if l[0] <= r[0] {
tmp = append(tmp, l[0])
l = l[1:]
} else {
tmp = append(tmp, r[0])
r = r[1:]
}
}
return tmp
}
func MergeSort(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
l := MergeSort(s[:n])
r := MergeSort(s[n:])
return merge(l, r)
}
func MergeSortSmart(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
var wg sync.WaitGroup
wg.Add(2)
var l, r []int
select {
case semaphore <- pass{}:
go func() {
l = MergeSortSmart(s[:n])
<-semaphore
wg.Done()
}()
default:
l = MergeSort(s[:n])
wg.Done()
}
select {
case semaphore <- pass{}:
go func() {
r = MergeSortSmart(s[n:])
<-semaphore
wg.Done()
}()
default:
r = MergeSort(s[n:])
wg.Done()
}
wg.Wait()
return merge(l, r)
}
func MergeSortSmartBug(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
var wg sync.WaitGroup
wg.Add(2)
l := mergeSmart(s[:n], &wg)
r := mergeSmart(s[n:], &wg)
wg.Wait()
return merge(l, r)
}
func mergeSmart(s []int, wg *sync.WaitGroup) []int {
var tmp []int
select {
case semaphore <- pass{}:
go func() {
tmp = MergeSortSmartBug(s)
<-semaphore
wg.Done()
}()
default:
tmp = MergeSort(s)
wg.Done()
}
return tmp
}
Why does the Bug version returns an empty slice? How can I refactor the Smart version without doing two selects one after the other?
Sorry for I couldn't reproduce this behavior in a smaller example.
The problem is not with the WaitGroup itself. It's with your concurrency handling. Your mergeSmart function lunches a go routine and returns the tmp variable without waiting for the go routine to finish.
You might want to try a pattern more like this:
leftchan := make(chan []int)
rightchan := make(chan []int)
go mergeSmart(s[:n], leftchan)
go mergeSmart(s[n:], rightchan)
l := <-leftchan
r := <-rightchan
Or you can use a single channel if order doesn't matter.
mergeSmart doesn't wait on the wg, so it returns a tmp that hasn't received a value yet. You could probably repair it by passing a reference to the destination slice in to the function, instead of returning a slice.
Look at the mergeSmart function. When the select enter into the first case, the goroutine is launched and imediatly returns tmp (which is an empty array). In that case there is no way to get the right value. (See advanced debugging prints here https://play.golang.org/p/IedaY3muso2)
Maybe passing arrays preallocated by reference?
I implemented both suggestions (passing slice by reference and using channels) and the (working!) result is here: https://play.golang.org/p/DcDC_-NjjAH
package main
import (
"fmt"
"math/rand"
"runtime"
"sync"
)
type pass struct{}
var semaphore = make(chan pass, runtime.NumCPU())
func main() {
rand.Seed(10)
s := make([]int, 16)
for i := 0; i < 16; i++ {
s[i] = int(rand.Int31n(1000))
}
fmt.Println(s)
fmt.Println(MergeSort(s))
fmt.Println(MergeSortSmart(s))
fmt.Println(MergeSortSmartPointer(s))
fmt.Println(MergeSortSmartChan(s))
}
func merge(l, r []int) []int {
tmp := make([]int, 0, len(l)+len(r))
for len(l) > 0 || len(r) > 0 {
if len(l) == 0 {
return append(tmp, r...)
}
if len(r) == 0 {
return append(tmp, l...)
}
if l[0] <= r[0] {
tmp = append(tmp, l[0])
l = l[1:]
} else {
tmp = append(tmp, r[0])
r = r[1:]
}
}
return tmp
}
func MergeSort(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
l := MergeSort(s[:n])
r := MergeSort(s[n:])
return merge(l, r)
}
func MergeSortSmart(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
var wg sync.WaitGroup
wg.Add(2)
var l, r []int
select {
case semaphore <- pass{}:
go func() {
l = MergeSortSmart(s[:n])
<-semaphore
wg.Done()
}()
default:
l = MergeSort(s[:n])
wg.Done()
}
select {
case semaphore <- pass{}:
go func() {
r = MergeSortSmart(s[n:])
<-semaphore
wg.Done()
}()
default:
r = MergeSort(s[n:])
wg.Done()
}
wg.Wait()
return merge(l, r)
}
func MergeSortSmartPointer(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
var l, r []int
var wg sync.WaitGroup
wg.Add(2)
mergeSmartPointer(&l, s[:n], &wg)
mergeSmartPointer(&r, s[n:], &wg)
wg.Wait()
return merge(l, r)
}
func mergeSmartPointer(tmp *[]int, s []int, wg *sync.WaitGroup) {
select {
case semaphore <- pass{}:
go func() {
*tmp = MergeSortSmartPointer(s)
<-semaphore
wg.Done()
}()
default:
*tmp = MergeSort(s)
wg.Done()
}
}
func MergeSortSmartChan(s []int) []int {
if len(s) <= 1 {
return s
}
n := len(s) / 2
lchan := make(chan []int)
rchan := make(chan []int)
go mergeSmartChan(s[:n], lchan)
go mergeSmartChan(s[n:], rchan)
l := <-lchan
r := <-rchan
return merge(l, r)
}
func mergeSmartChan(s []int, c chan []int) {
select {
case semaphore <- pass{}:
go func() {
c <- MergeSortSmartChan(s)
<-semaphore
}()
default:
c <- MergeSort(s)
}
}
I understood 100% what I was doing wrong, thanks!
And for future references, here's the benchmark of sorting a slice of 100,000 elems:
$ go test -bench=.
goos: linux
goarch: amd64
cpu: Intel(R) Core(TM) i5-9300H CPU # 2.40GHz
BenchmarkMergeSort-8 97 12230309 ns/op
BenchmarkMergeSortSmart-8 181 7209844 ns/op
BenchmarkMergeSortSmartPointer-8 163 7483136 ns/op
BenchmarkMergeSortSmartChan-8 156 8149585 ns/op

Slicing while unpacking slice

I have the following code
func Sum(a []int) int {
res := 0
for _, n := range a {
res += n
}
return res
}
func SumAll(ns ...[]int) (sums []int) {
for _, s := range ns {
sums = append(sums, Sum(s))
}
return
}
//SumAllTails sums [:1] in each slice
func SumAllTails(sls ...[]int) []int {
newsls := [][]int{}
for _, sl := range sls {
newsls = append(newsls, sl[1:])
}
return SumAll(newsls...)
}
Ideally I'd like to change the last function to be something like this
func SumAllTails(sls ...[]int) []int {
return SumAll(sls[:][1:]...)
}
This last bit returns each slice but the first one, but what I'd like to do is unpack each slice from position 1 onwards, omitting the value at 0. Is there a way of achieving this in go?
I think the only way to do what you want without going through the slices first is to write a SumAlln function:
func SumAlln(n int, ns ...[]int) (sums []int) {
for _, s := range ns {
sums = append(sums, Sum(s[n:]))
}
return
}
func SumAll(ns...[]int) []int {
return SumAlln(0,ns...)
}
And then call SumAlln.

go routine for range over channels

I have been working in Golang for a long time. But still I am facing this problem though I know the solution to my problem. But never figured out why is it happening.
For example If I have a pipeline situation for inbound and outbound channels like below:
package main
import (
"fmt"
)
func main() {
for n := range sq(sq(gen(3, 4))) {
fmt.Println(n)
}
fmt.Println("Process completed")
}
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
It does not give me a deadlock situation. But if I remove the go routine inside the outbound code as below:
func sq(in <-chan int) <-chan int {
out := make(chan int)
for n := range in {
out <- n * n
}
close(out)
return out
}
I received a deadlock error. Why is it so that looping over channels using range without go routine gives a deadlock.
This situation caused of output channel of sq function is not buffered. So sq is waiting until next function will read from output, but if sq is not async, it will not happen (Playground link):
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func main() {
numsCh := gen(3, 4)
sqCh := sq(numsCh) // if there is no sq in body - we are locked here until input channel will be closed
result := sq(sqCh) // but if output channel is not buffered, so `sq` is locked, until next function will read from output channel
for n := range result {
fmt.Println(n)
}
fmt.Println("Process completed")
}
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int, 100)
for n := range in {
out <- n * n
}
close(out)
return out
}
Your function creates a channel, writes to it, then returns it. The writing will block until somebody can read the corresponding value, but that's impossible because nobody outside this function has the channel yet.
func sq(in <-chan int) <-chan int {
// Nobody else has this channel yet...
out := make(chan int)
for n := range in {
// ...but this line will block until somebody reads the value...
out <- n * n
}
close(out)
// ...and nobody else can possibly read it until after this return.
return out
}
If you wrap the loop in a goroutine then both the loop and the sq function are allowed to continue; even if the loop blocks, the return out statement can still go and eventually you'll be able to connect up a reader to the channel.
(There's nothing intrinsically bad about looping over channels outside of goroutines; your main function does it harmlessly and correctly.)
The reason of the deadlock is because the main is waiting for the sq return and finish, but the sq is waiting for someone read the chan then it can continue.
I simplified your code by removing layer of sq call, and split one sentence into 2 :
func main() {
result := sq(gen(3, 4)) // <-- block here, because sq doesn't return
for n := range result {
fmt.Println(n)
}
fmt.Println("Process completed")
}
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
for n := range in {
out <- n * n // <-- block here, because no one is reading from the chan
}
close(out)
return out
}
In sq method, if you put code in goroutine, then the sq will returned, and main func will not block, and consume the result queue, and the goroutine will continue, then there is no block any more.
func main() {
result := sq(gen(3, 4)) // will not blcok here, because the sq just start a goroutine and return
for n := range result {
fmt.Println(n)
}
fmt.Println("Process completed")
}
func gen(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func sq(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n // will not block here, because main will continue and read the out chan
}
close(out)
}()
return out
}
The code is a bit complicated,
Let's simplify
First eq below, not has deadLock
func main() {
send := make(chan int)
receive := make(chan int)
go func() {
send<-3
send<-4
close(send)
}()
go func() {
receive<- <-send
receive<- <-send
close(receive)
}()
for v := range receive{
fmt.Println(v)
}
}
Second eq below,remove "go" has deadLock
func main() {
send := make(chan int)
receive := make(chan int)
go func() {
send<-3
send<-4
close(send)
}()
receive<- <-send
receive<- <-send
close(receive)
for v := range receive{
fmt.Println(v)
}
}
Let's simplify second code again
func main() {
ch := make(chan int)
ch <- 3
ch <- 4
close(ch)
for v := range ch{
fmt.Println(v)
}
}
The reason of the deadlock is no buffer channel waiting in main goroutine.
Two Solutions
// add more cap then "channel<-" time
func main() {
ch := make(chan int,2)
ch <- 3
ch <- 4
close(ch)
for v := range ch{
fmt.Println(v)
}
}
//async "<-channel"
func main() {
ch := make(chan int)
go func() {
for v := range ch {
fmt.Println(v)
}
}()
ch <- 3
ch <- 4
close(ch)
}
My understanding is
when the main thread is blocked for waiting for the chan to be writen or read, Go will detect if any other Go routine is running. If there is no any other Go routine running, it will have "fatal error: all goroutines are asleep - deadlock!"
I tested if by using the below simple case
func main() {
c := make(chan int)
go func() {
time.Sleep(10 * time.Second)
}()
c <- 1
}
The deadlock error is reported after 10 seconds.

goroutine value return order [duplicate]

This question already has answers here:
Golang channel output order
(4 answers)
Closed 4 years ago.
Why following codes always return 2,1, not 1,2.
func test(x int, c chan int) {
c <- x
}
func main() {
c := make(chan int)
go test(1, c)
go test(2, c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y)
}
If you want to know what the order is, then let your program include ordering information
This example uses a function closure to generate a sequence
The channel returns a struct of two numbers, one of which is a sequence order number
The sequence incrementer should be safe across go routines as there is a mutex lock on the sequence counter
package main
import (
"fmt"
"sync"
)
type value_with_order struct {
v int
order int
}
var (
mu sync.Mutex
)
func orgami(x int, c chan value_with_order, f func() int) {
v := new(value_with_order)
v.v = x
v.order = f()
c <- *v
}
func seq() func() int {
i := 0
return func() int {
mu.Lock()
defer mu.Unlock()
i++
return i
}
}
func main() {
c := make(chan value_with_order)
sequencer := seq()
for n := 0; n < 10; n++ {
go orgami(1, c, sequencer)
go orgami(2, c, sequencer)
go orgami(3, c, sequencer)
}
received := 0
for q := range c {
fmt.Printf("%v\n", q)
received++
if received == 30 {
close(c)
}
}
}
second version where the sequence is called from the main loop to make the sequence numbers come out in the order that the function is called
package main
import (
"fmt"
"sync"
)
type value_with_order struct {
v int
order int
}
var (
mu sync.Mutex
)
func orgami(x int, c chan value_with_order, seqno int) {
v := new(value_with_order)
v.v = x
v.order = seqno
c <- *v
}
func seq() func() int {
i := 0
return func() int {
mu.Lock()
defer mu.Unlock()
i++
return i
}
}
func main() {
c := make(chan value_with_order)
sequencer := seq()
for n := 0; n < 10; n++ {
go orgami(1, c, sequencer())
go orgami(2, c, sequencer())
go orgami(3, c, sequencer())
}
received := 0
for q := range c {
fmt.Printf("%v\n", q)
received++
if received == 30 {
close(c)
}
}
}

Resources