[英]Deadlock with buffered channel
我有一些代码是作业调度程序,正在整理来自许多TCP套接字的大量数据。 这段代码是使用大量瞬态对象的方法的结果-避免了争用,并且在很大程度上降低了CPU使用率并且现在锁定也不是问题。
我的应用程序有时会锁定,并且“通道长度”日志是唯一重复发生的事情,因为仍然有数据从我的套接字输入。 但是,计数仍为5000,并且不进行任何下游处理。
我认为问题可能是竞赛情况,并且可能挂断的线路是jobDispatcher
select
中的channel <- msg
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
}
}
}
更新01/20/16
我尝试将channelMap
用作带有一些互斥锁的全局对象,但最终仍然死锁。
稍微调整了代码,仍然锁定,但我不知道这是怎么做的! https://play.golang.org/p/PGpISU4XBJ
更新01/21/17在提出一些建议之后,我将其放入一个独立的工作示例中,以便人们看到。 https://play.golang.org/p/88zT7hBLeD
这是一个长期运行的过程,因此在游乐场将其杀死时,需要在计算机上本地运行。 希望这可以帮助您找到根底!
我猜测您的问题在另一个goroutine正在执行shutdown <- id
的同时,正在执行此channel <- msg
-msg。
由于channel
和shutdown
通道均未缓冲,因此它们阻塞了等待接收器的时间。 他们可以陷入僵局,等待另一边变得可用。
有几种解决方法。 您可以使用缓冲区1声明这两个通道。
或者,您可以执行Google的上下文包的操作,并通过关闭关闭通道来发送关闭信号,而不是通过发送关闭消息来发出信号。 查看https://golang.org/pkg/context/,尤其是WithCancel
, WithDeadline
和Done
函数。
您可能可以使用上下文来删除自己的关闭通道和超时代码。
JimB提出了在可能仍在通道上接收goroutine的同时将其关闭的观点。 您应该执行的操作是发送关闭消息(或关闭或取消上下文),并继续处理消息,直到关闭您的ch
通道(以case msg, ok := <-ch:
,这将在发送方收到关机信息。
这样,您将获得所有传入的消息,直到实际发生关闭为止,并且应该避免再次出现死锁。
我是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
您不是在这里放一些东西(无缓冲吗?)
channel, ok := channelMap[msg.Avr]
因此,在添加味精之前,您是否需要清空该频道?
channel <- msg
就像我说的那样,我是Go的新手,所以我希望自己不要傻。 :)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.