去GC停止我的goroutine?

时间:2015-10-27 09:28:42

标签: go garbage-collection

我一直试图从更传统的语言(如Java和C)进入Go,到目前为止,我一直在享受Go提供的经过深思熟虑的设计选择。当我开始我的第一个“真正的”项目时,我遇到了几乎没有人似乎有的问题。

我的项目是一个简单的网络实现,可以发送和接收数据包。一般结构是这样的(当然简化):

客户端使用服务器管理net.Conn。此Client会创建PacketReaderPacketWriter。这些都在不同的goroutine中运行无限循环。 PacketReader采用由客户端实现的单个OnPacketReceived函数的接口。

PacketReader代码如下所示:

go func() {
    for {
        bytes, err := reader.ReadBytes(10) // Blocks the current routine until bytes are available.
        if err != nil {
            panic(err) // Handle error
        }
        reader.handler.OnPacketReceived(reader.parseBytes(bytes))
    }
}()

PacketWriter代码如下所示:

go func() {

    for {
        if len(reader.packetQueue) > 0 {
            // Write packet
        }
    }
}()

为了阻止Client,客户端会创建一个由OnPacketReceived填充的频道,如下所示:

type Client struct {
    callbacks map[int]chan interface{}
    // More fields
}

func (c *Client) OnPacketReceived(packet *Packet) {
    c.callbacks[packet.Id] <- packet.Data
}

func (c *Client) SendDataBlocking(id int, data interface{}) interface{} {
    c.PacketWriter.QueuePacket(data)
    return <-c.callbacks[id]
}

现在这是我的问题:reader.parseBytes函数执行一些密集的解码操作,创建了很多对象(到了GC决定运行的程度)。然而, GC会暂停正在解码字节的读取器goroutine,然后挂起here描述了与我的相似的问题。我已经确认它实际上是GC导致它,因为运行它GOGC=off成功运行。

此时,我的3个例程看起来像这样:
- Client (main routine):等待频道
- Writer:仍在运行,等待队列中的新数据
- Reader:设置为可运行但未实际运行

不知何故,GC要么无法停止所有例程才能运行,要么在停止后不再恢复所述goroutine。

所以我的问题是:有没有办法解决这个问题?我是Go的新手所以我真的不知道我的设计选择是否是远程传统的,我都是改变我的程序结构。我是否需要更改处理数据包读取回调的方式,是否需要尝试使数据包解码器不那么密集?谢谢!

编辑:我正在运行Go 1.5.1,我会在今天晚些时候尝试一个有效的例子。

1 个答案:

答案 0 :(得分:1)

根据mrd0ll4r的评论,将作者更改为使用频道而不是片段(我甚至不知道为什么我首先这样做)。这似乎给了GC足够的“移动性”以允许线程停止。添加runtime.Gosched()并且仍然使用切片也有效,但是频道看起来更像“go-esque”。