英文:
throw all goroutines are asleep - deadlock! ------- Error in Google's GO
问题
我想写三个并发的go例程,它们彼此之间发送整数。现在,我的代码已经正确编译,但是在第一次执行后出现错误“throw: all goroutines are asleep - deadlock!”。我尝试找到错误,但是我无法在代码逻辑中找到任何错误。有人可以帮我找到代码中的错误吗?我的代码如下。
package main
import "math/rand"
func Routine1(command12 chan int, response12 chan int, command13 chan int, response13 chan int) {
// z12是一个变量,用于存储从通道2接收到的值,z13是一个变量,用于存储从通道3接收到的值。
z12 := 200
z13 := 200
m12 := false
m13 := false
y := 0
for i := 0; i < 20; i++ {
y = rand.Intn(100)
// 如果y的值不为0,则根据是否为质数将该值发送到例程2或3。
// 如果y的值为0,则保存进程状态(它使用的变量即z12、z13)和通道状态。[例程1是发起者]
if y == 0 {
print(z12, " z12 STATE SAVED\n")
print(z13, " z13 STATE SAVED\n")
// 例程1是发起者,它发送0以使其他进程保存状态。
y = 0
command12 <- y
command13 <- y
// 在例程2和3都没有发送0之前,进程1处于通道保存状态(它的进程状态已经保存)。
// 当例程1从其他进程接收到0时,保存通道并返回到它的常规例程过程。
// 当例程1从任何其他进程接收到0时,停止保存它们之间的通道。
// m12、m13用于标记是否接收到0。
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")
}
}
}
}
// 保存进程状态后,返回到正常行为。
m12 = false
m13 = false
}
if y != 0 {
// 如果y的值不为0,则例程1只是根据是否为质数将整数发送到其他进程,并相应地接收整数。
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)
}
// 例程2(或3)不是发起者(意味着它不能发送0)。当它从例程1或3接收到0时,它保存进程状态和接收到的通道的状态)。
// 当它从其他两个例程接收到0时,它保存所有通道状态并返回到它的常规行为。[例程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() {
// 创建三个并发通道以相互传递整数。
// command12用于从例程1发送整数,response12用于从例程2接收整数。
// response12用于从例程2发送整数,command12用于从例程1接收整数。{其他通道也是如此}
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)
}
英文:
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)
}
答案1
得分: 3
正如其他人所说-你的代码对我来说太复杂了,我无法快速找出它的预期逻辑。无论如何,“技术分析”方法带来了一些小的线索。当将Gosched添加为select语句的默认情况,并使通道缓冲时,代码就不会再发生死锁。虽然我不知道它在做什么,也不知道它是否按照你的意愿执行。
从代码上看,我觉得行为是不确定的(?)。无论如何,我认为原始代码可能是设计上的问题(例如,一些循环看起来像是忙等待,尽管它们运行了一个硬编码的N次,抱歉这么说)。
“工作”(==谁知道它在做什么)的代码:http://play.golang.org/p/dcUpeJ9EUa
PS:第325行的缓冲区大小常量不能低于4(通过每周试运行),并且似乎提供了改变代码行为的另一种方式。
英文:
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.
答案2
得分: 1
我不知道你问题的答案,但是Routine3
中的switch
语句看起来有问题,因为它包含了两个相同的case
语句(这让我想知道为什么6g不会对这段代码报错)。
以下是一些建议,可以使你的代码更易读:
- 正如Evan已经指出的,尽量为你的变量取更具描述性的名称。代码中使用
if someConditionIsMet
比使用if m23 == false
更容易理解。 - 通过将共同部分提取到函数中来使你的代码更干净。
- 删除无用的代码,比如将布尔值设置为true,然后检查它是否为true,或者检查奇数是否等于零。
- 考虑使用
else
而不是if <condition> {...}; if <negated condition> {...}
我建议你尝试编写单元测试,详细描述你的函数的预期行为。这不仅可以帮助你找到错误,还可以提高你的编码技巧。根据我的经验,编写时考虑测试的代码通常比未经测试的代码更易于理解、维护和演进。
愉快的编码
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论