I am trying to understand goroutines.
In the following example, why do 1)--4) behave differently?
See https://play.golang.org/p/_XXZe47W53v
package main
import (
"fmt"
"time"
)
func send(x int, ch chan int) {ch<-x}
func read(ch chan int) {fmt.Println(<-ch)}
func main() {
ch := make(chan int)
go read(ch) // 1) works
go send(1,ch) // -> 1
// go fmt.Println(<-ch) // 2) fatal error: all goroutines are asleep - deadlock!
// go send(1,ch) // isn't this the same as the above ?
// go send(1,ch) // 3) works
// go fmt.Println(<-ch) // -> 1
// go fmt.Println(<-ch) // 4) fatal error: all goroutines are asleep - deadlock!
// go send(1,ch) // why does the order of the go routine calls matter?
time.Sleep(100*time.Millisecond)
}
You are seeing the errors because the read is not happening inside of the goroutine, but in the main thread.
The line:
go fmt.Println(<-ch)
is evaluating the parameter in the main thread, and once it succeeds it will run the Println in the goroutine with the already resolved parameter. Since the code can never write to the ch in this state, it deadlocks.
You can observe this by changing it to:
go func() { fmt.Println(<-ch) }()
Then it will create a closure around the ch, and then the anonymous routine will be blocked, not the main thread, which can continue on to the send().
Related
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 am trying to understand the difference between calling goroutine with/without anonymous function.
When I try below code with anonymous function it works.
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go func() {
fmt.Println(<-ch)
}()
go send(1, ch)
time.Sleep(100 * time.Millisecond)
}
Below code without a anonymous function fails with deadlock.
go fmt.Println(<-ch) //fatal error: all goroutines are asleep - deadlock!
The code is available here
The Go Programming Language Specification
Receive operator
For an operand ch of channel type, the value of the receive operation
<-ch is the value received from the channel ch. The channel direction
must permit receive operations, and the type of the receive operation
is the element type of the channel. The expression blocks until a
value is available.
For example,
package main
import "fmt"
func main() {
ch := make(chan int)
go fmt.Println(<-ch)
ch <- 1
}
Playground: https://play.golang.org/p/K3_V92NRWvY
Output:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
// At prog.go: line 7: (<-ch)
fmt.Println(<-ch) evaluates its arguments, a receive on ch. There is no send pending for ch. fmt.Println(<-ch) blocks until a value is available, which never happens, it never gets to ch <- 1.
It is equivalent to:
package main
import "fmt"
func main() {
ch := make(chan int)
arg := <-ch
go fmt.Println(arg)
ch <- 1
}
Playground: https://play.golang.org/p/1wyVTe-8tyB
Output:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
// At prog.go: line 7: arg := <-ch
I have some code where i am trying to print the content of a channel in a goroutine using fmt.Println. Here is the simplified version of code.
package main
import "fmt"
import "time"
func main() {
ch := make(chan int)
go fmt.Println(<-ch);
ch<- 10;
time.Sleep(time.Second * 10);
}
When I run the above code I am getting this error.
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
C:/cygwin64/home/vmadhusudana/go/channel.go:9 +0x67
exit status 2
However it works fine when I call fmt.Println from a function
package main
import "fmt"
import "time"
func main() {
ch := make(chan int)
go func(){fmt.Println(<-ch)}();
ch<- 10;
time.Sleep(time.Second * 10);
}
What is making the difference here?
In the first version, the channel read is performed from the main go-routine - hence the deadlock.
The second version, the read is done from the created go-routine.
Basically this:
go fmt.Println(<-ch);
becomes:
v := <-ch
go fmt.Println(v);
As the parameters to the function are evaluated before it's invocation.
P.S. The defer statement behaves similarly. So always use a closure if you want the value passed to a go-routine or defer statement to be evaluated at "runtime".
// receive the value from channel firstly
// then create new goroutine to println
go fmt.Println(<-ch)
// receive channel in new goroutine
go func(){fmt.Println(<-ch)}()
https://play.golang.org/p/xMyqd-Yr8_a
this will help you to understand the order of exec.
The following code logged an error:
fatal error: all goroutines are asleep - deadlock!
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}
But when I changed the code into this:
package main
import "fmt"
func assign (ch chan int) {
ch <- 1
}
func main() {
ch := make(chan int)
go assign (ch)
fmt.Println(<-ch)
}
"1" was printed out.
Then I used buffered channels:
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2
fmt.Println(<-ch)
fmt.Println(<-ch)
}
"1" and "2" can also be printed out.
I'm a little confused about the situation. Thanks in advance!
Why the deadlock happened:
In the first code snippet you have only one main goroutine and it is blocked when you are trying to write into the channel here:
ch <- 1
Because nobody reads from the channel and the main goroutine is waiting for this to continue.
See Effective Go -> Channels
If the channel is unbuffered, the sender blocks until the receiver has received the value.
The sender is main function, the receiver is also main function.
How to avoid the deadlock:
In order to solve this, you have two options:
Option 1: make the ch channel buffered like this:
ch := make(chan int, 1) // buffer length is set to 1
From A Tour of Go
Sends to a buffered channel block only when the buffer is full.
So, you can write to the channel until the buffer is full. Then somebody has to start reading from the channel.
Option 2: write to the channel from a goroutine, like you did in the second code snippet:
func assign(ch chan int) {
ch <- 1
}
func main() {
ch := make(chan int)
go assign(ch) // does not block the main goroutine
fmt.Println(<-ch) // waiting to read from the channel
}
In this case main function will be executed until fmt.Println(<-ch) and continues as soon as it can read from the channel.
When you are using unbuffered channel, goroutine is blocked during write until someone does the read.
In your first snippet, there is an unbuffered channel and single goroutine (main goroutine).
So when you are trying to write:
ch <- 1
Nobody reads from the channel yet. The main goroutine is blocked and this line is never executed:
fmt.Println(<-ch)
That's why you've got the deadlock error.
In the second example, you still using unbuffered channel, which means write operation blocks the goroutine.
But by using go you are running the second goroutine.
It means even if this new goroutine will be blocked during write (in your assign function), the main goroutine will continue to work and fmt.Println(<-ch) will be executed and do the read (which in turn unblock background goroutine and assign function will finally reach the end).
To get more understanding about channels and goroutines, this snippet will give the same result (as your second snippet):
package main
import "fmt"
func print(ch chan int) {
fmt.Println(<-ch)
}
func main() {
ch := make(chan int)
go print(ch)
ch <- 1
}
When you are working with a buffered channel (third snippet), you can do N write operations without blocking goroutine (where N is the size of the buffer).
That's why in your example you did 2 writes without blocking and is able to read them later. But if your buffer is less than the count of write operations, and nobody do the read, you will fall into the same blocking issues (see the explanation of 1&2 snippets).