I am currently learming Go. I am readging the book An Introduction to programming in go
I am at the concurrency section and form what I understand I can see two way to define an infinite loop a go program.
func pinger(c chan string) {
for i := 0; ; i++ {
c <- "ping"
}
}
func printer(c chan string) {
for {
msg := <- c
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
I am wondering what is the use of the i variable in the pinger function. What is the best "go" way to declare an infinite loop ? I would say the the one in the printer function is better but as I am new to I might miss something with the declaration in the pinger function.
Thanks for all people who will help.
The i in the first loop is redundant; it's always best to get rid of unused variables therefore You should use a for{} in the pinger() function as well.
Here is a working example:
package main
import(
"time"
"fmt"
)
func main() {
c := make(chan string)
go printer(c)
go pinger(c)
time.Sleep(time.Second * 60)
}
func pinger(c chan string) {
for{
c <- "ping"
}
}
func printer(c chan string) {
for {
msg := <- c
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
Run on playground
The "best" way is to write code that is easy to read and maintain. Your variable i in func pinger serves no purpose and someone stumbling upon that code later on will have a hard time understand what it's for.
I would just do
func pinger(c chan string) {
for {
c <- "ping"
}
}
Related
i am new in golang recently. i have a question about goroutine when use time.sleep function. here the code.
package main
import (
"fmt"
"time"
)
func go1(msg_chan chan string) {
for {
msg_chan <- "go1"
}
}
func go2(msg_chan chan string) {
for {
msg_chan <- "go2"
}
}
func count(msg_chan chan string) {
for {
msg := <-msg_chan
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
func main() {
var c chan string
c = make(chan string)
go go1(c)
go go2(c)
go count(c)
var input string
fmt.Scanln(&input)
}
and output is
go1
go2
go1
go2
go1
go2
i think when count function is execute sleep function, go1 and go2 will execute in random sequence. so the out put maybe like
go1
go1
go2
go2
go2
go1
when i delete the sleep code in count function. the result as i supposed , it's random.
i am stucked in this issue.
thanks.
First thing to notice is that there are three go routines and all of them are independent of each other. The only thing that combines the two go routines with count routine is the channel on which both go routines are sending the values.
time.Sleep is not making the go routines synchronous. On using time.Sleep you are actually letting the count go routine to wait for that long which let other go routine to send the value on the channel which is available for the count go routine to be able to receive.
One more thing that you can do to check it is increase the number of CPU's which will give you the random result.
func GOMAXPROCS(n int) int
GOMAXPROCS sets the maximum number of CPUs that can be executing
simultaneously and returns the previous setting. If n < 1, it does not
change the current setting. The number of logical CPUs on the local
machine can be queried with NumCPU. This call will go away when the
scheduler improves.
The number of CPUs available simultaneously to executing goroutines is
controlled by the GOMAXPROCS shell environment variable, whose default
value is the number of CPU cores available. Programs with the
potential for parallel execution should therefore achieve it by
default on a multiple-CPU machine. To change the number of parallel
CPUs to use, set the environment variable or use the similarly-named
function of the runtime package to configure the run-time support to
utilize a different number of threads. Setting it to 1 eliminates the
possibility of true parallelism, forcing independent goroutines to
take turns executing.
Considering the part where output of the go routine is random, it is always random. But channels most probably work in queue which is FIFO(first in first out) as it depends on which value is available on the channel to b received. So whichever be the value available on the channel to be sent is letting the count go routine to wait and print that value.
Take for an example even if I am using time.Sleep the output is random:
package main
import (
"fmt"
"time"
)
func go1(msg_chan chan string) {
for i := 0; i < 10; i++ {
msg_chan <- fmt.Sprintf("%s%d", "go1:", i)
}
}
func go2(msg_chan chan string) {
for i := 0; i < 10; i++ {
msg_chan <- fmt.Sprintf("%s%d", "go2:", i)
}
}
func count(msg_chan chan string) {
for {
msg := <-msg_chan
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
func main() {
var c chan string
c = make(chan string)
go go1(c)
go go2(c)
go count(c)
time.Sleep(time.Second * 20)
fmt.Println("finished")
}
This sometimes leads to race condition which is why we use synchronization either using channels or wait.groups.
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func go1(msg_chan chan string) {
defer wg.Done()
for {
msg_chan <- "go1"
}
}
func go2(msg_chan chan string) {
defer wg.Done()
for {
msg_chan <- "go2"
}
}
func count(msg_chan chan string) {
defer wg.Done()
for {
msg := <-msg_chan
fmt.Println(msg)
time.Sleep(time.Second * 1)
}
}
func main() {
var c chan string
c = make(chan string)
wg.Add(1)
go go1(c)
wg.Add(1)
go go2(c)
wg.Add(1)
go count(c)
wg.Wait()
fmt.Println("finished")
}
Now coming to the part where you are using never ending for loop to send the values on a channel. So if you remove the time.Sleep your process will hang since the loop will never stop to send the values on the channel.
I have the following sample code. I want to maintain 4 goroutines running at all times. They have the possibility of panicking. In the case of the panic, I have a recover where I restart the goroutine.
The way I implemented works but I am not sure whether its the correct and proper way to do this. Any thoughts
package main
import (
"fmt"
"time"
)
var gVar string
var pCount int
func pinger(c chan int) {
for i := 0; ; i++ {
fmt.Println("adding ", i)
c <- i
}
}
func printer(id int, c chan int) {
defer func() {
if err := recover(); err != nil {
fmt.Println("HERE", id)
fmt.Println(err)
pCount++
if pCount == 5 {
panic("TOO MANY PANICS")
} else {
go printer(id, c)
}
}
}()
for {
msg := <-c
fmt.Println(id, "- ping", msg, gVar)
if msg%5 == 0 {
panic("PANIC")
}
time.Sleep(time.Second * 1)
}
}
func main() {
var c chan int = make(chan int, 2)
gVar = "Preflight"
pCount = 0
go pinger(c)
go printer(1, c)
go printer(2, c)
go printer(3, c)
go printer(4, c)
var input string
fmt.Scanln(&input)
}
You can extract the recover logic in a function such as:
func recoverer(maxPanics, id int, f func()) {
defer func() {
if err := recover(); err != nil {
fmt.Println("HERE", id)
fmt.Println(err)
if maxPanics == 0 {
panic("TOO MANY PANICS")
} else {
go recoverer(maxPanics-1, id, f)
}
}
}()
f()
}
And then use it like:
go recoverer(5, 1, func() { printer(1, c) })
Like Zan Lynx's answer, I'd like to share another way to do it (although it's pretty much similar to OP's way.) I used an additional buffered channel ch. When a goroutine panics, the recovery function inside the goroutine send its identity i to ch. In for loop at the bottom of main(), it detects which goroutine's in panic and whether to restart by receiving values from ch.
Run in Go Playground
package main
import (
"fmt"
"time"
)
func main() {
var pCount int
ch := make(chan int, 5)
f := func(i int) {
defer func() {
if err := recover(); err != nil {
ch <- i
}
}()
fmt.Printf("goroutine f(%v) started\n", i)
time.Sleep(1000 * time.Millisecond)
panic("goroutine in panic")
}
go f(1)
go f(2)
go f(3)
go f(4)
for {
i := <-ch
pCount++
if pCount >= 5 {
fmt.Println("Too many panics")
break
}
fmt.Printf("Detected goroutine f(%v) panic, will restart\n", i)
f(i)
}
}
Oh, I am not saying that the following is more correct than your way. It is just another way to do it.
Create another function, call it printerRecover or something like it, and do your defer / recover in there. Then in printer just loop on calling printerRecover. Add in function return values to check if you need the goroutine to exit for some reason.
The way you implemented is correct. Just for me the approach to maintain exactly 4 routines running at all times looks not much go_way, either handling routine's ID, either spawning in defer which may leads unpredictable stack due to closure. I don't think you can efficiently balance resource this way. Why don't you like to simple spawn worker when it needed
func main() {
...
go func(tasks chan int){ //multiplexer
for {
task = <-tasks //when needed
go printer(task) //just spawns handler
}
}(ch)
...
}
and let runtime do its job? This way things are done in stdlib listeners/servers and them known to be efficient enough. goroutines are very lightweight to spawn and runtime is quite smart to balance load. Sure you must to recover either way. It is my very personal opinion.
I was trying to implement an example Go code for using returned channels from go routines without any "reading block" in the main function. Here, a fanIn function accepts channels from two other routines and return which got as input.
Here, the expected output is Random Outputs from two inner routines. But the actual output is always one "ann" followed by a "john", not at all random in any case.
Why am I not getting random output?
Go Playground: http://play.golang.org/p/46CiihtPwD
Actual output:
you say: ann,0
you say: john,0
you say: ann,1
you say: john,1
......
Code:
package main
import (
"fmt"
"time"
)
func main() {
final := fanIn(boring("ann"), boring("john"))
for i := 0; i < 100; i++ {
fmt.Println("you say:", <-final)
}
time.Sleep(4 * time.Second)
}
func boring(msg string) chan string {
c1 := make(chan string)
go func() {
for i := 0; ; i++ {
c1 <- fmt.Sprintf("%s,%d", msg, i)
time.Sleep(time.Second)
}
}()
return c1
}
func fanIn(input1, input2 <-chan string) chan string {
c := make(chan string)
go func() {
for {
c <- <-input1
}
}()
go func() {
for {
c <- <-input2
}
}()
return c
}
No particular reason, that's just how Go happens to schedule the relevant goroutines (basically, you're getting "lucky" that there's a pattern). You can't rely on it. If you really want an actual reliably random result, you'll have to manually mix in randomness somehow.
There's also the Multiplex function from https://github.com/eapache/channels/ (doc: https://godoc.org/github.com/eapache/channels#Multiplex) which does effectively the same thing as your fanIn function. I don't think it would behave any differently in terms of randomness though.
I started to learn go language days ago. When I tried to start writing some fun codes, I am stuck by a strange behavior.
package main
import "fmt"
func recv(value int) {
if value < 0 {
return
}
fmt.Println(value)
go recv(value-1)
}
func main() {
recv(10)
}
when I run the above code, only 10 is printed. When I remove the go before the call to recv, 10 to 0 are printed out. I believe I am misusing go routine here, but I can not understand why it failed start a go routine this way.
When the main function returns, Go will not wait for any still existing goroutines to finish but instead just exit.
recv will return to main after the first "iteration" and because main has nothing more to do, the program will terminate.
One solution to this problem is to have a channel that signals that all work is done, like the following:
package main
import "fmt"
func recv(value int, ch chan bool) {
if value < 0 {
ch <- true
return
}
fmt.Println(value)
go recv(value - 1, ch)
}
func main() {
ch := make(chan bool)
recv(10, ch)
<-ch
}
Here, recv will send a single boolean before returning, and main will wait for that message on the channel.
For the logic of the program, it does not matter what type or specific value you use. bool and true are just a straightforward example. If you want to be more efficient, using a chan struct{} instead of a chan bool will save you an additional byte, since empty structs do not use any memory.
A sync.Waitgroup is another solution and specifically intended for the purpose of waiting for an arbitrary amount of goroutines to run their course.
package main
import (
"fmt"
"sync"
)
func recv(value int, wg *sync.WaitGroup) {
if value < 0 {
return
}
fmt.Println(value)
wg.Add(1) // Add 1 goroutine to the waitgroup.
go func() {
recv(value-1, wg)
wg.Done() // This goroutine is finished.
}()
}
func main() {
var wg sync.WaitGroup
recv(10, &wg)
// Block until the waitgroup signals
// all goroutines to be finished.
wg.Wait()
}
I did so and also worked. How come?
package main
import "fmt"
func recv(value int) {
if value < 0 {
return
}
fmt.Println(value)
recv(value - 1)
}
func main() {
recv(10)
}
I am trying to create a simple program to learn channels in Go.
But I´m running in to a deadlock error, which I can´t figure out
package main
import (
"fmt"
"time"
)
func printer(c chan int) {
for i := 0; i < 10; i++ {
c <- i
time.Sleep(time.Second)
}
}
func reciever(c chan int) {
for {
recievedMsg := <-c
fmt.Println(recievedMsg)
}
}
func main() {
newChanel := make(chan int)
printer(newChanel)
reciever(newChanel)
}
My initial thoughts was something about the Sleep function, but even if I don´t include this I still run into this error and exit message.
Can anyone give some hints on how to solve this?
Thanks in advance
You need two execution threads because now there is no way for the reciever function to be called as you never leave the printer function. You need to execute one of them on a separate goroutine.
You should also close the channel and use the range operator in your loop, so that it ends when the channel is closed.
So I propose you this code :
func printer(c chan int) {
for i := 0; i < 10; i++ {
c <- i
time.Sleep(time.Second)
}
close(c)
}
func reciever(c chan int) {
for recievedMsg := range c {
fmt.Println(recievedMsg)
}
}
func main() {
newChanel := make(chan int)
go printer(newChanel)
reciever(newChanel)
}