Channel slice of integers - go

I want to create a slice that is a channel and contains integers.
test := make(chan []int)
test <- 5
This is the way I initialized it but I have no idea how to now pass a value since for slices we would use append but for channels we send data using <-
I have tried with both just <- and append and both combined like below and can't get it to work
test <- append(test, 5)

You defined a channel of []int but are trying to send it an int. You have to send it a slice of ints and then have the receiver use that slice.
A working example is here: https://play.golang.org/p/TmcUKU8G-1
Notice that I'm appending the 4 to the things slice and not the channel itself
package main
import (
"fmt"
)
func main() {
c := make(chan []int)
things := []int{1, 2, 3}
go func() {
c <- things
}()
for _, i := range <-c {
fmt.Println(i)
}
go func() {
c <- append(things, 4)
}()
for _, i := range <-c {
fmt.Println(i)
}
}
Output:
1
2
3
1
2
3
4

This is called
buffered channel
the right syntaxis is
test := make(chan int, 5)
test <- 1
test <- 2
Golang tour has an example:
https://tour.golang.org/concurrency/3

Related

Channel returns the same value twice on receive

Hello I have the following simple program:
I have a function printNums(nums chan int) which should take 3 numbers from a channel and print them concurrently. Each printing is done in a new goroutine inside the range statement.
However when I run this program, instead of expected output 4, 12, 32, i get 12, 12, 32. Can you help me find out where is the problem and why i don't receive from channel same values that have been sent to channel? Thank you.
package main
import ("fmt")
func printNums(nums chan int){
c := make(chan struct{}, 100)
for num := range(nums){
go func(){
fmt.Println(num)
c <- struct{}{}
}()
}
<-c
<-c
<-c
}
func main(){
nums := make(chan int)
go func() {
nums <- 4
nums <- 12
nums <- 32
close(nums)
}()
printNums(nums)
}
You are printing the current value of num instead of the value num was when the goroutine was created. The loop variable is overwritten at every turn, and the goroutine may see the updated value.
for num := range(nums){
go func(x int){
fmt.Println(x)
c <- struct{}{}
}(num)
}

Select all values from multiple channels when channel ready

I'm new in golang and I faced with the problem.
I have several channels.
Some payload gets to this channels in different time.
How I can get all values one by one from channels in the time when channel ready to spit it.
For example I wrote this code:
package main
import (
"fmt"
"time"
"math/rand"
)
func main() {
arr1 := []int8{1,2,3,4,5}
arr2 := []int8{6,7,8,9,10}
c1 := make(chan int8)
c2 := make(chan int8)
go func() {
for _, val := range arr1 {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
c1 <- val
}
}()
go func() {
for _, val := range arr2 {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
c2 <- val
}
}()
select {
case res1 := <- c1:
fmt.Println(res1)
case res2 := <- c2:
fmt.Println(res2)
}
fmt.Println("Hello, test")
}
But in this case, I get only first value, from one of the channels.
Please, give me advise how to solve my issue.
Link to go-play https://play.golang.org/p/FOmkP57YCyR
You have to do couple of things.
1) Make sure you close channels once you are done with source.
2) Iterate over channels until it's closed.
Example:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
arr1 := []int8{1, 2, 3, 4, 5}
arr2 := []int8{6, 7, 8, 9, 10}
c1 := make(chan int8)
c2 := make(chan int8)
go func() {
for _, val := range arr1 {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
c1 <- val
}
close(c1)
}()
go func() {
for _, val := range arr2 {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
c2 <- val
}
close(c2)
}()
_c1 := true
_c2 := true
var res1, res2 int8
for _c1 == true || _c2 == true {
select {
case res1, _c1 = <-c1:
if _c1 == true {
fmt.Println(res1)
}
case res2, _c2 = <-c2:
if _c2 == true {
fmt.Println(res2)
}
}
}
fmt.Println("Hello, test")
}
On execution, I got following output on screen.
6
1
7
2
3
4
8
5
9
10
Hello, test
You don't have to use 2 channels. Just use 1 channel and store values into it from multiple goroutines. Channel is lightweight thread connector that is fast and can be instantiated multiple times to store values from multiple goroutines.
The problem with your code is that it has no loop to loop over values from goroutines channel. You only print it once using select. select makes other goroutines wait until it executes one of possible cases it has. If all of the cases are possible, then it selects randomly to executes.
The reason you only got one value from your channel is because when your goroutines are working, they store values from the array to channel sequentially. while this happen, you call select statement in your main thread with the cases to get the values from the channel in your goroutines. Since you don't loop over the channels, you'll only get one value from the channel which is the value it firstly received. In this case, you loop array sequentially into channel in your goroutines, hence you'll get the first index of the array since it's the value that will be firstly sent to select statement in main thread. All of your select cases are possible to be executed, hence it will execute one of the case randomly and you'll get first index in one of those arrays.
To fix this, you need to loop over the channel to get the values stored in it one by one. Beside, you also need to synchronize all the threads in order to avoid deadlock conditions, which is happened when your main thread doesn't know when to stop invoking channel from goroutines since they work asynchronously. Your channel is ready to spit the value right after it gets its value inside goroutines and invoked into main thread in loop. Here is the code:
package main
import (
"fmt"
"time"
"math/rand"
"sync"
)
// writer set numbers from array to channel
func writer(ch chan int, arr []int ,wgwrite *sync.WaitGroup) {
defer wgwrite.Done()
for _, val := range arr {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
ch <- val
}
}
// reader receive input from writer channels and print them all
func reader(ch chan int, wgread *sync.WaitGroup) {
defer wgread.Done()
for i:= range ch {
fmt.Println(i)
}
fmt.Println("Hello, test")
}
func main() {
arr1 := []int{1,2,3,4,5}
arr2 := []int{6,7,8,9,10}
ch := make(chan int)
wgwrite := &sync.WaitGroup{}
wgread := &sync.WaitGroup{}
wgwrite.Add(2)
go writer(ch, arr1, wgwrite)
go writer(ch, arr2, wgwrite)
wgread.Add(1)
go reader(ch, wgread)
wgwrite.Wait()
close(ch)
wgread.Wait()
}
https://play.golang.org/p/32Fgetq_Zu7
Hope it helps.
Select does not wait for go routines. To achieve that you should wrap it in for statement. In this way select will be running until one of the cases returns and breaks out of for statement.
for {
select {
...
You can also use buffered channels which are non-blocking and wait groups. Like this:
arr1 := []int8{1,2,3,4,5}
arr2 := []int8{6,7,8,9,10}
c1 := make(chan int8, len(arr1))
c2 := make(chan int8, len(arr2))
var wg sync.WaitGroup
wg.Add(1) // First wait group
go func() {
for _, val := range arr1 {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
c1 <- val
}
wg.Done()
}()
wg.Add(1) // Second wait group
go func() {
for _, val := range arr2 {
time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
c2 <- val
}
wg.Done()
}()
// executed after wg.Done() is called 2 times since we have 2 wait groups
wg.Wait()
// We are not writing to channels anymore so we can close them.
close(c1)
close(c2)
for value := range c1 {
fmt.Println(value)
}
for value := range c2 {
fmt.Println(value)
}
fmt.Println("Hello, test")

Go: transformed channel

Let's say I have an int channel in Go:
theint := make(chan int)
I want to wrap this channel in a new channel called incremented
incremented := make(chan int)
Such that:
go func() { theint <- 1 }
<- incremented // 2
appended can be assumed to be the only one that reads from the int.
It will work if a run a goroutine in the background
go func() {
for num := range theint {
incremented <- num + 1
}
}
However, I prefer to do it without a goroutine since I can't control it in my context.
Is there a simpler way to do it?
One thing that came to mind is python's yield:
for num in theint:
yield num + 1
Is something like this possible in go?
Generator pattern
What you are trying to implement is generator pattern. To use channels and goroutines for implementation of this pattern is totally common practice.
However, I prefer to do it without a goroutine since I can't control it in my context.
I believe the problem is deadlock
fatal error: all goroutines are asleep - deadlock!
To avoid deadlocks and orphaned (not closed) channels use sync.WaitGroup. This is an idiomatic way to control goroutines.
Playground
package main
import (
"fmt"
"sync"
)
func incGenerator(n []int) chan int {
ch := make(chan int)
var wg sync.WaitGroup
wg.Add(len(n))
for _, i := range n {
incremented := i + 1
go func() {
wg.Done()
ch <- incremented
}()
}
go func() {
wg.Wait()
close(ch)
}()
return ch
}
func main() {
n := []int{1, 2, 3, 4, 5}
for x := range incGenerator(n) {
fmt.Println(x)
}
}
One thing you can also consider is having a select on the int channel and an exit channel - in an infinite for loop. You can choose a variable increment value too. Please see code below:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var accum int //accumulator of incremented values
var wg sync.WaitGroup
c1 := make(chan int)
exChan := make(chan bool)
wg.Add(1)
go func() {
time.Sleep(time.Second * 1)
c1 <- 1
wg.Done()
}()
wg.Add(1)
go func() {
time.Sleep(time.Second * 2)
c1 <- 2
wg.Done()
}()
wg.Add(1)
go func() {
time.Sleep(time.Second * 2)
c1 <- 5
wg.Done()
}()
go func() {
wg.Wait()
close(exChan)
}()
for {
var done bool
select {
case incBy := <-c1: //Increment by value in channel
accum += incBy
fmt.Println("Received value to increment:", incBy, "; Accumulated value is", accum)
case d := <-exChan:
done = !(d)
}
if done == true {
break
}
}
fmt.Println("Final accumulated value is", accum)
}
Playground: https://play.golang.org/p/HmdRmMCN7U
Exit channel not needed, if we are having non-zero increments always. I like #I159 's approach too!
Anyways, hope this helps.

Add all the items of a slice into a channel

In Go, is there a more idiomatic way to add all of the elements of an array/slice into a channel than the following?
ch := make(chan string)
values := []string{"lol", "cat", "lolcat"}
go func() {
for _, v := range values {
ch <- v
}
}()
I was looking for something like ch <- values... but that is rejected by the compiler.
Until iterators will come along, yes, the code you wrote is as idiomatic as it gets. I have it packaged for reuse as something like this in codebases I work on:
// ToChan returns a channel containing all elements in the slice s.
// The channel is closed when all elements are consumed from the channel.
func ToChan[T any](s []T) <-chan T {
ch := make(chan T, len(s))
for _, e := range s {
ch <- e
}
close(ch)
return ch
}
https://go.dev/play/p/c5v4df_M1IG
A for/range loop is the idiomatic way to send all of the elements of a slice to a channel:
for _, v := range values {
ch <- v
}
It is not necessary to run the for loop in a goroutine, as shown in the question.
You can declare a chan of string arrays, unless you absolutely want to keep a chan of strings :
package main
import "fmt"
func main() {
ch := make(chan []string)
values := []string{"lol", "cat", "lolcat"}
go func() {
ch <- values
}()
fmt.Printf("Values : %+v\n", <-ch)
}

Slice append from channels

I want to create slice and add values returned from channel.
Below is the code I tried but could not able to solve it.
I have to send address of the slice, but I am not able to figure out how :(
package main
import "fmt"
import "time"
func sendvalues(cs chan int){
for i:=0;i<10;i++{
cs<-i
}
}
func appendInt(cs chan int, aINt []int)[]*int{
for {
select {
case i := <-cs:
aINt = append(aINt,i)//append returns new type right ?
fmt.Println("slice",aINt)
}
}
}
func main() {
cs := make(chan int)
intSlice := make([]int, 0,10)
fmt.Println("Before",intSlice)
go sendvalues(cs)
go appendInt(cs,intSlice)// I have to pass address here
time.Sleep(999*999999)
fmt.Println("After",intSlice)
}
Your code won't work for two (in fact three) reasons:
append returns a new slice as soon as the capacity is reached.
Thus, the assignment in appendInt will do nothing.
appendInt runs concurrently, therefore:
As long as appendInt does not message main that it is finished,
main does not know when the intSlice has all the values you want.
You have to wait for all goroutines to return at the end of main
Problem 1: Modifying slices in functions
You may know that in Go every value you pass to a function is copied. Reference values,
such as slices, are copied too, but have pointers internally which then point to the original memory location. That means you can modify the elements of a slice in a function. What you
can't do is reassigning this value with a new slice as the internal pointer would point to somewhere different. You need pointers for that. Example (Play):
func modify(s *[]int) {
for i:=0; i < 10; i++ {
*s = append(*s, i)
}
}
func main() {
s := []int{1,2,3}
modify(&s)
fmt.Println(s)
}
Problem 2: Synchronizing goroutines
To wait for started goroutines, you can use a sync.WaitGroup. Example (Play):
func modify(wg *sync.WaitGroup, s *[]int) {
defer wg.Done()
for i:=0; i < 10; i++ {
*s = append(*s, i)
}
}
func main() {
wg := &sync.WaitGroup{}
s := []int{1,2,3}
wg.Add(1)
go modify(wg, &s)
wg.Wait()
fmt.Println(s)
}
The example above waits (using wg.Wait()) for modify to finish
(modify calls wg.Done() when finished). If you remove the wg.Wait() call, you will
see why not synchronizing is a problem. Comparison of outputs:
With wg.Wait(): [1 2 3 0 1 2 3 4 5 6 7 8 9]
Without wg.Wait(): [1 2 3]
The main goroutine returns earlier than the modify goroutine which is why you will never
see the modified results. Therefore synchronizing is absolutely necessary.
A good way to communicate the new slice would be to use a channel. You would not need to
use pointers and you would have synchronization. Example (Play):
func modify(res chan []int) {
s := []int{}
for i:=0; i < 10; i++ {
s = append(s, i)
}
res <- s
}
func main() {
c := make(chan []int)
go modify(c)
s := <-c
fmt.Println(s)
}

Resources