我有一些代码是作业调度程序,并且正在从大量TCP套接字中整理大量数据。这段代码是对Large number of transient objects - avoiding contention方法的一种结果,它在很大程度上与CPU使用量有很大关系并且现在也没有锁定问题。
我的应用程序会不时锁定并且“通道长度”日志是唯一不断重复的东西,因为数据仍然来自我的套接字。然而,计数仍为5000,并且没有进行下游处理。
我认为这个问题可能是一个竞争条件,而channel <- msg
select
内jobDispatcher
可能会挂起它。麻烦的是我无法弄清楚如何验证这一点。
我怀疑select可以随机取项,goroutine正在返回,shutdownChan没有机会处理。然后数据点击inboundFromTCP并阻止!
有人可能会在这里看到一些非常明显的错误。并希望提供解决方案!?
var MessageQueue = make(chan *trackingPacket_v1, 5000)
func init() {
go jobDispatcher(MessageQueue)
}
func addMessage(trackingPacket *trackingPacket_v1) {
// Send the packet to the buffered queue!
log.Println("Channel length:", len(MessageQueue))
MessageQueue <- trackingPacket
}
func jobDispatcher(inboundFromTCP chan *trackingPacket_v1) {
var channelMap = make(map[string]chan *trackingPacket_v1)
// Channel that listens for the strings that want to exit
shutdownChan := make(chan string)
for {
select {
case msg := <-inboundFromTCP:
log.Println("Got packet", msg.Avr)
channel, ok := channelMap[msg.Avr]
if !ok {
packetChan := make(chan *trackingPacket_v1)
channelMap[msg.Avr] = packetChan
go processPackets(packetChan, shutdownChan, msg.Avr)
packetChan <- msg
continue
}
channel <- msg
case shutdownString := <-shutdownChan:
log.Println("Shutting down:", shutdownString)
channel, ok := channelMap[shutdownString]
if ok {
delete(channelMap, shutdownString)
close(channel)
}
}
}
}
func processPackets(ch chan *trackingPacket_v1, shutdown chan string, id string) {
var messages = []*trackingPacket_v1{}
tickChan := time.NewTicker(time.Second * 1)
defer tickChan.Stop()
hasCheckedData := false
for {
select {
case msg := <-ch:
log.Println("Got a messages for", id)
messages = append(messages, msg)
hasCheckedData = false
case <-tickChan.C:
messages = cullChanMessages(messages)
if len(messages) == 0 {
messages = nil
shutdown <- id
return
}
// No point running checking when packets have not changed!!
if hasCheckedData == false {
processMLATCandidatesFromChan(messages)
hasCheckedData = true
}
case <-time.After(time.Duration(time.Second * 60)):
log.Println("This channel has been around for 60 seconds which is too much, kill it")
messages = nil
shutdown <- id
return
}
}
}
2016年1月20日更新
我尝试使用channelMap
作为一个具有一些互斥锁定的全局重做,但它最终仍然陷入僵局。
略微调整代码,仍然锁定,但我不知道这个怎么做! https://play.golang.org/p/PGpISU4XBJ
更新01/21/17 在提出一些建议后,我将其放入一个独立的工作示例中,以便人们可以看到。 https://play.golang.org/p/88zT7hBLeD
这是一个长时间运行的过程,因此当操场将其杀死时,需要在机器上本地运行。希望这有助于深入了解它!
答案 0 :(得分:2)
我猜你的问题在与其他goroutine正在进行channel <- msg
同时执行此shutdown <- id
时遇到困难。
由于channel
和shutdown
通道都没有被缓冲,它们会阻止等待接收器。而且他们可以陷入僵局,等待另一方变得可用。
有几种方法可以解决它。您可以使用缓冲区1声明这两个通道。
或者通过发送关闭消息而不是发信号,您可以执行Google的上下文包所做的操作,并通过关闭关闭通道发送关闭信号。请查看https://golang.org/pkg/context/,尤其是WithCancel
,WithDeadline
和Done
函数。
您可以使用上下文删除自己的关闭通道和超时代码。
JimB有一个关于关闭goroutine的观点,因为它可能仍然在频道上接收。您应该做的是发送关闭消息(或关闭,或取消上下文)并继续处理消息,直到您的ch
通道关闭(检测到case msg, ok := <-ch:
),这将在关机后发生收件人收到。
这样你就可以获得所有传入的消息,直到关闭实际发生,并且应该避免第二次死锁。
答案 1 :(得分:-2)
我是Go的新手,但在此代码中
case msg := <-inboundFromTCP:
log.Println("Got packet", msg.Avr)
channel, ok := channelMap[msg.Avr]
if !ok {
packetChan := make(chan *trackingPacket_v1)
channelMap[msg.Avr] = packetChan
go processPackets(packetChan, shutdownChan, msg.Avr)
packetChan <- msg
continue
}
channel <- msg
Aren你在这里放了一些东西(无缓冲?)
channel, ok := channelMap[msg.Avr]
所以你不需要清空那个频道才能在这里添加msg吗?
channel <- msg
就像我说的,我是Go的新手,所以我希望自己不会傻瓜。 :)