【问题标题】:what would cause code blocking in go?什么会导致代码阻塞?
【发布时间】:2013-10-27 09:06:18
【问题描述】:

由于 go 是一种并发程序语言,因此使用channel(我几乎所有代码都使用它)或其他东西来同步goroutine

而且我还知道 go 使用 scheduler 来调度 goroutine,这意味着你应该在每个 goroutine 中调用 scheduler(channel action, runtime.goSche 或其他东西) 并保证它会被执行。

以上是我目前对go 的所有限制,我使用它们来设计我的代码。

但我也发现它会在我的代码中发生代码阻塞。而且很难找到导致阻塞的原因(即使使用 GDB)。

我错过了什么吗?

还有什么可能导致阻塞?

我应该注意什么?

[编辑]:好的,因为我的项目的代码有点大。我决定不显示 标准 go 代码,只显示可能导致代码阻塞的部分的一般概念。以下是我项目中文件传输模块的一些代码片段:

func(c *client)listenRead() {
    for {
        _ := websocket.JSON.Receive(c.ws, &package)
        switch package.Op {
        case fileUpld:
            c.fileMan.store <- package.Body
        case fileDownld:
            c.fileMan.downld <- package.Body
        case c.xxx:
            ...
        default:
            // bad package.
        }
    }
}

一个client通过websocket从另一个client监听和接收消息,然后根据packageOp字段将包转发到不同的handler ,例如,fileManager 如下所示。

func(fm *fileManager) fileRouter() {
    for {
        select {
            case fs := <-fm.fileUpld:
                if window < filesize {
                    f.Write(fs.content) // client A write to file
                    window += fs.contSize
                } else {
                    f.close()           // close file
                }
            case fd := fm.downld:
                go fm.downldFile(fd)
        }
    }
}

chan fileUpld 获取从client 发送的数据时,fileManger 可以接收文件片段并存储到服务器。当它收到来自客户端的request 时,它可以下载文件,它会生成一个goroutine 来完成这项工作,如下所示:

func(fm *fileManager) downldFile(fd) {
    f := getFile(fd)                     // get the file that client A write
    b := make([]byte, SeqLength)
    for {
        if convergence < window {
            f.Read(b)

            // wrap 'b' to package 'p', for send

            fm.server.send <- p   // send to client B
        } else if window < fileSize {
            runtime.Goshed()
        } else {
            // download done.
            fm.done <- xx
            return
        }
    }
}

我的文件传输模块的主要思想是客户端A想向客户端B发送文件,而不想等待来自client Bacceptance,这意味着它将首先存储到服务器,client B稍后将通过从服务器下载文件来获取文件。但可能客户端 B 下载文件,而客户端 A 上传文件。所以上传和下载之间需要同步。

我使用三个变量来实现:windowconvergencefilesize。当client A上传(写入)文件时,window会增加它写入的字节数。而当client B下载(读取)文件时,convergence会增加它读取的字节数。 Write 仅发生 window &lt; filesizeread 仅发生 convergence &lt; window。就像:

+-----------+---------------+--------------+
|############||||||||||||||||              |
+-----------+---------------+--------------+
            ^               ^              ^
            |               |              |
       convergence        window        filesize

我知道这会导致数据争用,但它也保证read 不会读取write 尚未写入的内容。(顺便说一句:我没有使用channel 来实现它的解决方案。)

当我尝试同时上传和下载时发生代码阻塞(我放慢了客户端A来实现它)。但是当我将GOMAXPROCS设置为2时就没有问题了。

【问题讨论】:

  • 如果您有一个代码示例来演示您正在谈论的问题,那么您会更容易理解您的要求。如果您可以让示例在 play.golang.org 上运行,那就更好了。
  • @MatrixForg :我重新编辑并添加了代码示例。希望你更清楚。

标签: concurrency go


【解决方案1】:

您应该考虑使用垃圾收集器来暂停您的整个程序,即所谓的“停止世界”。在具有大量内存消耗的大型应用程序上,GC 可以“停止世界”长达 10 秒,但这是最坏的情况。这是关于 Go 中 GC 的一个很好的答案:What kind of Garbage Collection does Go use?

【讨论】:

  • 感谢您的回答,这对我来说是新事物。但实际上有时它会超过 10 秒,而且我的应用程序不是一个更大的应用程序,这可能不是由大内存消耗引起的。我想知道代码的结构考虑。
猜你喜欢
  • 2011-12-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-15
  • 1970-01-01
  • 1970-01-01
  • 2017-01-26
相关资源
最近更新 更多