throw all goroutines are asleep - deadlock! ----— Error in Google's GO
-
09-03-2021 - |
質問
I want to write three concurrent go routines that sends integers to each other. Now, my code is compiled properly, however after first execution it gives error "throw: all goroutines are asleep - deadlock!". I tried to find the error but I could not able to find any error in code logic.Can anybody help me to find the mistake with my code. My code is given below.
package main
import "rand"
func Routine1(command12 chan int, response12 chan int, command13 chan int, response13 chan int) {
// z12 is a variable which stores the value comming from channel 2 and z13 is a variable which stores the value comming from channel 3.
z12 := 200
z13 := 200
m12 := false
m13 := false
y := 0
for i := 0; i < 20; i++ {
y = rand.Intn(100)
// If y's value is not 0 then the value will be sent to routine 2 or 3 according to prime or not.
// If y's value is 0 then process state (the varibles used by it means z12, z13) and channel state will be saved.[routine 1 is initiator]
if y == 0 {
print(z12, " z12 STATE SAVED\n")
print(z13, " z13 STATE SAVED\n")
// Routine 1 is initiator, it sends 0 to make other process to save the state.
y = 0
command12 <- y
command13 <- y
// Untill routine 2 and 3 does not send 0, process 1 is on channel saving state (it's process state is already saved).
// When routine 1 recives 0 from both other processes, channel is saved and routine 1 retuns to it's common routine procedure.
// When routine 1 recives 0 from any other processes, saving channel bettwen them is stopped.
// m12, m13 is used to mark whether 0 recived or not.
for m12 != true || m13 != true {
select {
case cmd1 := <-response12:
{
z12 = cmd1
if z12 != 0 {
print(z12, " z12 Channel Saving.... \n")
y = rand.Intn(100)
command12 <- y
}
if z12 == 0 {
m12 = true
print(" z12 Channel Saving Stopped \n")
}
}
case cmd2 := <-response13:
{
z13 = cmd2
if z13 != 0 {
print(z13, " z13 Channel Saving.... \n")
y = rand.Intn(100)
command13 <- y
}
if z13 == 0 {
m13 = true
print(" z13 Channel Saving Stopped \n")
}
}
}
}
// After saving process state it retuns to it's normal behaviour.
m12 = false
m13 = false
}
if y != 0 {
// If y value is not 0, routine 1 just sends int to other process according to prime or not and recives int accordingly.
if y%2 == 0 {
command12 <- y
}
if y%2 != 0 {
command13 <- y
}
select {
case cmd1 := <-response12:
{
z12 = cmd1
print(z12, " z12\n")
}
case cmd2 := <-response13:
{
z13 = cmd2
print(z13, " z13\n")
}
}
}
}
close(command12)
close(command13)
}
//Routine 2 (or 3) is not an initiator (means it can't send 0). When it recives 0 (from routine 1 or 3) it save the state of process and the state of the channel from which it recived).
// When it recives 0 from both other two routine, it saves all channel state and returns to it's common behaviour. [same in routine 3]
func Routine2(command12 chan int, response12 chan int, command23 chan int, response23 chan int) {
z21 := 200
z23 := 200
m21 := false
m23 := false
for i := 0; i < 20; i++ {
select {
case x, open := <-command12:
{
if !open {
return
}
if x != 0 && m23 != true {
z21 = x
print(z21, " z21\n")
}
if x != 0 && m23 == true {
z21 = x
print(z21, " z21 Channel Saving \n")
}
if x == 0 {
m21 = true
if m21 == true && m23 == true {
print(" z21 and z23 Channel Saving Stopped \n")
m23 = false
m21 = false
}
if m21 == true && m23 != true {
z21 = x
print(z21, " z21 Channel Saved \n")
}
}
}
case x, open := <-response23:
{
if !open {
return
}
if x != 0 && m21 != true {
z23 = x
print(z23, " z21\n")
}
if x != 0 && m21 == true {
z23 = x
print(z23, " z23 Channel Saving \n")
}
if x == 0 {
m23 = true
if m21 == true && m23 == true {
print(" z23 Channel Saving Stopped \n")
m23 = false
m21 = false
}
if m23 == true && m21 != true {
z23 = x
print(z23, " z23 Channel Saved \n")
}
}
}
}
if m23 == false && m21 == false {
y := rand.Intn(100)
if y%2 == 0 {
if y == 0 {
y = 10
response12 <- y
}
}
if y%2 != 0 {
if y == 0 {
y = 10
response23 <- y
}
}
}
if m23 == true && m21 != true {
y := rand.Intn(100)
response12 <- y
}
if m23 != true && m21 == true {
y := rand.Intn(100)
command23 <- y
}
}
close(response12)
close(command23)
}
func Routine3(command13 chan int, response13 chan int, command23 chan int, response23 chan int) {
z31 := 200
z32 := 200
m31 := false
m32 := false
for i := 0; i < 20; i++ {
select {
case x, open := <-command13:
{
if !open {
return
}
if x != 0 && m32 != true {
z31 = x
print(z31, " z21\n")
}
if x != 0 && m32 == true {
z31 = x
print(z31, " z31 Channel Saving \n")
}
if x == 0 {
m31 = true
if m31 == true && m32 == true {
print(" z21 Channel Saving Stopped \n")
m31 = false
m32 = false
}
if m31 == true && m32 != true {
z31 = x
print(z31, " z31 Channel Saved \n")
}
}
}
case x, open := <-command23:
{
if !open {
return
}
if x != 0 && m31 != true {
z32 = x
print(z32, " z32\n")
}
if x != 0 && m31 == true {
z32 = x
print(z32, " z32 Channel Saving \n")
}
if x == 0 {
m32 = true
if m31 == true && m32 == true {
print(" z32 Channel Saving Stopped \n")
m31 = false
m32 = false
}
if m32 == true && m31 != true {
z32 = x
print(z32, " z32 Channel Saved \n")
}
}
}
}
if m31 == false && m32 == false {
y := rand.Intn(100)
if y%2 == 0 {
response13 <- y
}
if y%2 != 0 {
response23 <- y
}
}
if m31 == true && m32 != true {
y := rand.Intn(100)
response13 <- y
}
if m31 != true && m32 == true {
y := rand.Intn(100)
response23 <- y
}
}
close(response13)
close(response23)
}
func main() {
// Three concurrent channels are created to pass integers to each other.
// command 12 used to send int and response12 is used to receive int from routine 1 to routine 2.
// response 12 used to send int and command 12 is used to receive int from routine 2 to routine 1. {so as for others}
command12 := make(chan int)
response12 := make(chan int)
command13 := make(chan int)
response13 := make(chan int)
command23 := make(chan int)
response23 := make(chan int)
go Routine1(command12, response12, command13, response13)
go Routine2(command12, response12, command23, response23)
Routine3(command13, response13, command23, response23)
}
解決
As others said - your code is too complex for me to quickly find out its intended logic. Anyway a "technical analysis" approach brought some little bits. When adding Gosched as a default case to the select statements and making the channels buffered - then the code no more deadlocks. Though I have no idea what it is doing and if it does what you want it to do.
It seems to me, from looking at the code, like the behaviour is non deterministic(?). In any case, I think the original code is probably broken by design (e.g. some loops look like they are busy waiting even though they run a hardcoded N times, sic!), sorry to say that.
The "working" (== who knows what it's doing) code: http://play.golang.org/p/dcUpeJ9EUa
PS: The buffer size const @ line 325 can't drop bellow 4 (by trial runs with weekly) and seems to provide an another way to change the behavior of the code.
他のヒント
I don't know the answer to your problem, but the switch
statement in Routine3
looks buggy since it contains two identical case
statements (which makes me wonder why 6g does not complain about this code).
A few suggestions to make your code more legible:
- As Evan has already pointed out, try to come up with more descriptive names for your variables. Code that reads
if someConditionIsMet
is easier to understand thanif m23 == false
. - Dry your code by factoring common parts out into functions.
- Remove dead code like setting a boolean to true and then checking whether it is true or checking whether an odd number is equal to zero.
- Consider using
else
instead ofif <condition> {...}; if <negated condition> {...}
I would recommend to try to come up with unit tests which exhaustively describe the expected behavior of your functions. This will not only help you find the bug, but also improve your coding skills. From my experience, code written with tests in mind is often easier to understand, maintain and evolve than untested code.
Happy hacking :)