I read it on (https://www.geeksforgeeks.org/channel-in-golang/) that:
"In the channel, the send and receive operation block until another side is not ready by default.
It allows goroutine to synchronize with each other without explicit locks or condition variables."
To test above statement, I have written a sample program mentioned below:
Program:
package main
import (
"fmt"
"sync"
"time"
)
func myFunc(ch chan int) {
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) //<-- According to rule, control will be blocked here until 'ch' sends some data so that it will be received in our myFunc() go routine.
}
func main() {
fmt.Println("Start Main method")
// Creating a channel
ch := make(chan int)
go myFunc(ch) //<-- This go routine started in a new thread
time.Sleep(2 * time.Second) //<--- introduced a Sleep of 2 seconds to ensure that myFunc() go routine executes before main thread
ch <- 10
fmt.Println("End Main method")
}
I was expecting below output:
Start Main method
Inside goroutine:: myFunc()
20
End Main method
But, Actual output received is:
Start Main method
Inside goroutine:: myFunc()
End Main method
Why the value sent through channel is not printed?
I think, it is because main thread finished its execution first and hence, all other goroutine also terminated.
If that is the case, then, why does the rule said - It allows goroutine to synchronize with each other without explicit locks or condition variables.
Because, to get the expected output, I have to use sync.WaitGroup to tell the main thread to wait for the other goroutine to finish. Isn't it violating the above rule as I am using locks in form of waitgroup?
PS: I am learning golang. So please forgive if I get the concept totally wrong.
The main goroutine exists before the myFunc goroutine is able to print the output. Here is an implementation which ensures that myFunc goroutine finishes before the main goroutine exits.
package main
import (
"fmt"
"sync"
"time"
)
func myFunc(ch chan int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) //<-- According to rule, control will be blocked here until 'ch' sends some data so that it will be received in our myFunc() go routine.
}
func main() {
fmt.Println("Start Main method")
// Creating a channel
ch := make(chan int)
wg := sync.WaitGroup{}
wg.Add(1)
go myFunc(ch, &wg) //<-- This go routine started in a new thread
time.Sleep(2 * time.Second) //<--- introduced a Sleep of 2 seconds to ensure that myFunc() go routine executes before main thread
ch <- 10
wg.Wait()
fmt.Println("End Main method")
}
The channels are used here for synchronization and it works as described in documentation. It does not mean that the code starting from this point in the code will be executed at the same speed. It only means that main goroutine will not continue if myFunc goroutine is not reading from channel. And myFunc will wait for main goroutine to push data to channel. After this happen both goroutines will continue it execution independently.
Try this, used your code as basis
package main
import (
"fmt"
"time"
)
func myFunc(ch chan int, done chan struct{}) {
defer close(done) // channel will be closed in the function exit phase
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) //<-- According to rule, control will be blocked here until 'ch' sends some data so that it will be received in our myFunc() go routine.
}
func main() {
fmt.Println("Start Main method")
// Creating a channel
ch := make(chan int)
done := make(chan struct{}) // signal channel
go myFunc(ch, done) //<-- This go routine started in a new thread
time.Sleep(2 * time.Second) //<--- introduced a Sleep of 2 seconds to ensure that myFunc() go routine executes before main thread
ch <- 10
<-done // waiting for function complete
fmt.Println("End Main method")
}
Or use Jaroslaw's suggestion.
Because go is so fast... https://play.golang.org/p/LNyDAA3mGYY
After you send to channel scheduler isn't fast enoght... and program exists. I have introduced an additional context switcher for scheduler to show effect.
Yes, you are right
I think, it is because main thread finished its execution first and hence, all other goroutine also terminated.
If you check the above program execution. The sleep is before main thread writes to the channel. Now even though which goroutine() will have CPU time is completely arbitary, but in the above case if the main sleeps before the explicit sleep logic. myFunc will be blocked as there is no data in ch
Here I made a slight change to the above code to make main sleep after writing data into Channel. It gives the expected output, Without using waitgroup or quit channels.
package main
import (
"fmt"
"time"
)
func myFunc(ch chan int) {
fmt.Println("Inside goroutine:: myFunc()")
fmt.Println(10 + <-ch) //<-- According to rule, control will be blocked here until 'ch' sends some data so that it will be received in our myFunc() go routine.
}
func main() {
fmt.Println("Start Main method")
// Creating a channel
ch := make(chan int)
go myFunc(ch) //<-- This go routine started in a new thread
ch <- 10
time.Sleep(2 * time.Second) //<--- introduced a Sleep of 2 seconds to ensure that myFunc() go routine executes before main thread
fmt.Println("End Main method")
}
It is currently a race condition between the myFunc being able to print and your main function exiting.
If we look at the spec for program execution at
https://golang.org/ref/spec#Program_execution
Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.
It is still your job to make sure that all spawned goroutines will complete before your main goroutine exists.
In your case, you could use a waitgroup as you mentioned or you could use a done channel.
https://play.golang.org/p/RVr0HXuUMgn
Given your code you could also close the channel you use to send the integer over since you are passing it to the function as bidirectional but it's not strictly idiomatic.
https://play.golang.org/p/wGvexC5ZgIi
Related
package main
import (
"fmt"
)
type A struct{
exit chan bool
}
func (a *A) f(){
select{
//the routine process
//quit
case <- a.exit:
fmt.Println("-----over-----")
a.exit <- true
fmt.Println("+++++over++++++")
}
}
func main() {
a := A{}
go a.f()
a.exit = make(chan bool)
a.exit <- true
}
I'd like to run muti goroutines,and I want let the main func to notice other goroutine to quit.
here is my code,but the program block in the select,the program only output "-----over-----",without "+++++over++++++",what's wrong with the code?Grateful for your help.
Your program blocks because that is what you have written, consider this order of operations:
main goroutine starts a.f goroutine.
a.f blocks trying to read from the nil channel a.exit.
main sets a.exit to be an unbuffered channel, a.f is now blocked reading from the new channel, this is allowed.
main writes a value into a.exit and a.f reads the value from a.exit, this synchronises the goroutines, nether are blocked now.
a.f now blocks trying to write into the unbuffered a.exit, this will never unblock because nobody will ever try to read from the channel again.
main now exits and causes all other goroutines to exit, this might happen before step 5.
So the reasons your program never outputs +++++over++++++ are:
Your a.f goroutine blocks at a.exit <- true because no other goroutine will read this value from the channel.
Your main goroutine will probably exit and terminate the entire program before a.f can finish working.
I think you are asking how to make main exit after the goroutine is finished, this is the most simple example of that:
package main
import (
"fmt"
)
type A struct {
exit chan struct{}
}
func (a *A) f() {
defer close(a.exit) // Close the chanel after f() finishes, closed channels yield the zero-value so <-a.exit will unblock
fmt.Println("+++++over++++++")
}
func main() {
a := A{}
go a.f()
a.exit = make(chan struct{})
<-a.exit
}
golang: Why there's no deadlock in this code?
Please go through this following code:
package main
import (
"fmt"
"time"
)
func f1(done chan bool) {
done <- true
fmt.Printf("this's f1() goroutine\n")
}
func f2(done chan bool) {
val := <-done
fmt.Printf("this's f2() goroutine. val: %t\n", val)
}
func main() {
done1 := make(chan bool)
done2 := make(chan bool)
go f1(done1)
go f2(done2)
fmt.Printf("main() go-routine is waiting to see deadlock.\n")
time.Sleep(5 * time.Second)
}
As one can see, go-routine f1() is sending a value onto the channel. And go-routine f2() is receiving a value from a channel.
However, there's no go-routine receiving from the channel which is being sent onto by go-routine f1().
Similarly, there's no go-routine sending onto the channel which is being received from by go-routine f2().
As #icza's comment correctly states, a deadlock happens when all goroutines are stuck and can't make progress. In your case f1 and f2 are stuck, but the main goroutine is not - so this isn't a deadlock.
However, it is a goroutine leak! Goroutine leaks happen when some code finishes its logical existence but leaves goroutines running (unterminated). I've found tools like github.com/fortytw2/leaktest useful for detecting goroutine leaks, and it would detect the issue in your code - give it a try.
Here's a modified code sample:
import (
"testing"
"time"
"github.com/fortytw2/leaktest"
)
func f1(done chan bool) {
done <- true
fmt.Printf("this's f1() goroutine\n")
}
func f2(done chan bool) {
val := <-done
fmt.Printf("this's f2() goroutine. val: %t\n", val)
}
func TestTwoGoroutines(t *testing.T) {
defer leaktest.CheckTimeout(t, time.Second)()
done1 := make(chan bool)
done2 := make(chan bool)
go f1(done1)
go f2(done2)
}
When you run this test, you'll see something like:
--- FAIL: TestTwoGoroutines (1.03s)
leaktest.go:132: leaktest: timed out checking goroutines
leaktest.go:150: leaktest: leaked goroutine: goroutine 7 [chan send]:
leaktest-samples.f1(0xc000016420)
leaktest-samples/leaktest1_test.go:45 +0x37
created by leaktest-samples.TestTwoGoroutines
leaktest-samples/leaktest1_test.go:60 +0xd1
leaktest.go:150: leaktest: leaked goroutine: goroutine 8 [chan receive]:
leaktest-samples.f2(0xc000016480)
leaktest-samples/leaktest1_test.go:50 +0x3e
created by leaktest-samples.TestTwoGoroutines
leaktest-samples/leaktest1_test.go:61 +0xf3
As #icza said, the main goroutine can continue and ultimately terminate.
If you remove the go keyword from either of the two function calls (make them occur on the main goroutine) then the application has a deadlock. See this go playground (it highlights the deadlock for you) https://play.golang.org/p/r9qo2sc9LQA
There's one more thing to it.
func f1(done chan bool) {
fmt.Printf("f1()\n")
}
func main() {
done := make(chan bool)
go f1(done)
done <- true
}
Here, the caller go-routine clearly gets stuck since there's no go-routine in existence that's receiving from the channel.
Here, not that all the go-routines are blocked (f1() slips through), but there's still a dead-lock. The reason being, there has to be at least 1 go routine that should be receiving from the channel.
But this clearly contradicts the above comment and not all go-routines are blocked here.
main.go
func main() {
fmt.Println("hello")
ch := make(chan struct{}, 1)
<-ch
}
main_test.go
func Test_Main(t *testing.T) {
main()
}
go run main.go
hello
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
but
go test -v main_test.go -run=Test_Main
=== RUN Test_Main
hello
go test will not report an error and will always run.
After consulting a lot of information, I didn't find an answer to explain this phenomenon. Maybe my way is wrong?This channel method is used in projects.
Thanks.
When you run a regular program, it waits for input from channel. And because there is only one goroutine there is no way to receive the input from channel (no other thread to send to it). Thus deadlock is reported.
On the other hand test runner uses goroutines to execute tests. So there is more then one goroutine spawned and the deadlock is not detected (runtime assumes that other goroutine could send to channel).
To answer your question from comment: go run and go test are not supposed to achieve the same effects. go run executes your program, go test executes procedures that test your code. These commands executes two different programs.
I am not sure if you can detect this kind of errors (deadlocks) with tests.
Edit:
go test waits for test to finish (you can configure how long with -timeout d option). So I assume it spawns goroutine that waits for timer.Timer to expire, so there is no deadlock (there is always one goroutine that has a chance to be executed).
Edit2:
Try this program:
package main
import (
"fmt"
"time"
)
func main() {
go func() {
t := time.NewTimer(10 * time.Second)
<-t.C
}()
fmt.Println("hello")
ch := make(chan struct{}, 1)
<-ch
}
It waits 10 seconds before reporting deadlock.
Edit3:
Or take a look at flowing code that illustrates how test runner works:
package main
import (
"fmt"
"time"
)
func original_main_func() {
fmt.Println("hello")
ch := make(chan struct{}, 1)
<-ch
}
func test() {
original_main_func()
}
func test_runner() {
ch := make(chan struct{}, 1)
go func() {
test()
close(ch)
}()
t := time.NewTimer(10 * time.Second)
select {
case <-t.C:
panic("timeout")
case <-ch:
fmt.Println("test executed")
}
}
func main() {
test_runner()
}
I'm following a quick intro to Go and one of the examples is:
package main
import (
"fmt"
"time"
)
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
done <- true
}
func main() {
done := make(chan bool, 1)
go worker(done)
<-done
}
I understand whats occuring but I guess I'm not grasping the sequence of events or the limitations?
A channel is created called done with a buffer size of 1.
The channel is passed into a function
After the timer is complete it adds a true boolean to the channel
I'm not sure what the final <-done is doing though
from: https://gobyexample.com/channel-synchronization
Receiver operator <- followed by channel name (done in this case) is used to wait for a value written to channel from worker goroutine. (i.e this read operation will be blocking. If you omit <-done, main goroutine will exit immediately even before worker's goroutine start and you won't be able to see results)
You can do whatever you want with <-done as value: assign it to another variable, pass it as a parameter to another function or just ignore it as in your case... etc.
in the next example, I don't understand why end value not printed when received
package main
import "fmt"
func main() {
start := make(chan int)
end := make(chan int)
go func() {
fmt.Println("Start")
fmt.Println(<-start)
}()
go func() {
fmt.Println("End")
fmt.Println(<-end)
}()
start <- 1
end <- 2
}
I know sync.WaitGroup can solve this problem.
Because the program exits when it reaches the end of func main, regardless of whether any other goroutines are running. As soon as the second function receives from the end channel, main's send on that channel is unblocked and the program finishes, before the received value gets a chance to be passed to Println.
The end value is not printed because as soon as the main goroutine (the main function is actually a goroutine) is finished (in other terms get unblocked) the other non-main goroutines does not have the chance to get completed.
When the function main() returns, the program exits. Moreover goroutines are independent units of execution and when a number of them starts one after the other you cannot depend on when a goroutine will actually be started. The logic of your code must be independent of the order in which goroutines are invoked.
One way to solve your problem (and the easiest one in your case) is to put a time.Sleep at the end of your main() function.
time.Sleep(1e9)
This will guarantee that the main goroutine will not unblock and the other goroutines will have a change to get executed.
package main
import (
"fmt"
"time"
)
func main() {
start := make(chan int)
end := make(chan int)
go func() {
fmt.Println("Start")
fmt.Println(<-start)
}()
go func() {
fmt.Println("End")
fmt.Println(<-end)
}()
start <- 1
end <- 2
time.Sleep(1e9)
}
Another solution as you mentioned is to use waitgroup.
Apart from sleep where you have to specify the time, you can use waitgroup to make you program wait until the goroutine completes execution.
package main
import "fmt"
import "sync"
var wg sync.WaitGroup
func main() {
start := make(chan int)
end := make(chan int)
wg.Add(2)
go func() {
defer wg.Done()
fmt.Println("Start")
fmt.Println(<-start)
}()
go func() {
defer wg.Done()
fmt.Println("End")
fmt.Println(<-end)
}()
start <- 1
end <- 2
wg.Wait()
}