【问题标题】:Why do I have a deadlock even though it includes an endless loop?为什么即使它包含无限循环,我也会出现死锁?
【发布时间】:2018-12-11 21:32:27
【问题描述】:

所以我开始玩弄围棋,并且对围棋例程很着迷。我现在写了一个简单的测试,看看我是否可以在不断打印变量的同时改变它的值。

我现在有以下代码:

package main

import (
    "fmt"
    "time"
)

func change(c chan float64) float64 {
    time.Sleep(2 * time.Second)
    return 2.5
}

func main() {

    s := 1.1

    c := make(chan float64)
    go change(c)
    s = <-c

    for {
        fmt.Println(s)
        time.Sleep(100 * time.Millisecond)
    }    
}

不幸的是它以错误结束:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /home/kramer65/repos/go/src/messing_around/main.go:19 +0x7d
exit status 2

我四处搜索,发现这个死锁发生在 main 函数结束而 goroutines 还没有结束时。但由于我有一个无限循环,我不知道我的代码还有什么问题。

谁能告诉我这段代码有什么问题,以及我如何在不断打印出变量的同时更改它的值?欢迎所有提示!

【问题讨论】:

  • 因为没有发送到通道c,所以程序在从c 接收时永远阻塞。我猜你打算在change 函数中使用c &lt;- 2.5 而不是return 2.5
  • 在进入循环之前,您尝试在c 上接收,但在c 上没有发送任何内容。
  • 仔细查看堆栈跟踪,您会发现 main 永远不会进入 for 循环。 The Tour 逐步解释频道。 “当主函数结束而 goroutines 尚未结束时发生死锁”是不正确的。当 main 完成时,无论如何程序都会终止。

标签: go concurrency multiprocessing deadlock


【解决方案1】:

看来你可能对频道和套路有误解。

行:

go change(c)

似乎表明函数change 将写入c。然而,它最终只是在一段时间后返回一个值。

此值 (2.5) 不会在任何地方收到。此外,c 不会被写入任何地方。我怀疑您打算将2.5 写入频道c。其语法如下:

c<-2.5

因此,如果您将 change 函数更改为:

func change(c chan float64) {
    time.Sleep(2 * time.Second)
    c <- 2.5
}

您应该不会再看到死锁了。请注意,我不再返回 float64

我做了一个操场来确保这一点:https://play.golang.org/p/SgLiUmPpcAZ

评论更新

1.1 将始终被通道的值覆盖。但是,如果您想打印s 的初始值(如注释中所述),则必须稍微更改流程并使用select 语句:

package main

import (
    "fmt"
    "time"
)

func change(c chan float64) {
    time.Sleep(2 * time.Second)
    c <- 2.5
}

func main() {

    s := 1.1

    c := make(chan float64)
    go change(c)

    for {
        select {
        case s = <-c:
        default:
            // c isn't ready yet
        }
        fmt.Println(s)
        time.Sleep(100 * time.Millisecond)
    }
}

现在您有了select 语句,您也可以将它与time.Ticker 一起使用:

ticker := time.NewTicker(100 * time.Millisecond)
for {
    select {
    case s = <-c:
    case <-ticker.C:
        fmt.Println(s)
    default:
        // c isn't ready yet
    }
}

【讨论】:

  • 谢谢!这确实是我打算做的。您的代码并没有按照我的预期做。我希望它在仍然是 1.1 时开始打印出 s,让 change() 在 2 秒后更改 s 的值,并在打印出的值中看到这一点。所以我希望它打印出1.1 2 秒,之后打印的值变为2.5。所以我希望s = &lt;-c 行像回调一样工作,但事实并非如此。程序等待一个值从通道中出来,然后继续。知道如何让它在循环中间更新s 的值吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多