[英]Golang multiple timers with map+channel+mutex
所以我正在使用map / channel / mutex实现多个计时器。 为了取消计时器,我有一个存储取消信息的通道图,下面是代码:
var timerCancelMap = make(map[string]chan interface{})
var mutexLocker sync.Mutex
func cancelTimer(timerIndex string) {
mutexLocker.Lock()
defer mutexLocker.Unlock()
timerCancelMap[timerIndex] = make(chan interface{})
timerCancelMap[timerIndex] <- struct{}{}
}
func timerStart(timerIndex string) {
fmt.Println("###### 1. start timer: ", timerIndex)
timerStillActive := true
newTimer := time.NewTimer(time.Second * 10)
for timerStillActive {
mutexLocker.Lock()
select {
case <-newTimer.C:
timerStillActive = false
fmt.Println("OOOOOOOOO timer time's up: ", timerIndex)
case <-timerCancelMap[timerIndex]:
timerCancelMap[timerIndex] = nil
timerStillActive = false
fmt.Println("XXXXXXXXX timer canceled: ", timerIndex)
default:
}
mutexLocker.Unlock()
}
fmt.Println("###### 2. end timer: ", timerIndex)
}
func main() {
for i := 0; i < 10; i++ {
go timerStart(strconv.Itoa(i))
if i%10 == 0 {
cancelTimer(strconv.Itoa(i))
}
}
}
现在,这给了我deadlock
,如果我删除所有的mutex.lock / unlock,它将给我concurrent map read and map write
。 那我在做什么错?
我知道sync.Map解决了我的问题,但是性能却受到很大影响,因此我想坚持使用地图解决方案。
提前致谢!
这里发生了一些会导致脚本问题的事情:
cancelTimer创建一个没有缓冲区的通道make(chan interface {}),例如make(chan struct {},1)。 这意味着向该通道的发送将被阻塞,直到另一个goroutine尝试从该通道接收。 因此,当您尝试从主goroutine调用cancelTimer时,它将锁定MutexLocker,然后在发送取消消息时阻止,与此同时,其他goroutine无法锁定MutexLocker从取消通道接收消息,从而导致死锁。
添加缓冲区后,cancelTimer调用将立即返回。
然后,我们将遇到其他一些小问题。 首先是程序将立即退出而不打印任何内容。 发生这种情况是因为在启动测试goroutine并发送了cancel之后,主线程已经完成了所有工作,这告诉程序已完成。 因此,我们需要告诉主线程等待goroutines,其中sync.WaitGroup非常适合:
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
timerStart(strconv.Itoa(i))
}(i)
if i%10 == 0 {
cancelTimer(strconv.Itoa(i))
}
}
wg.Wait()
}
我可以看到您已经添加了MutexLocker来保护地图,后来又添加了for循环,从而为每个goroutine提供了获取MutexLocker来检查其计时器的机会。 这为计算机带来了很多工作,并使代码变得比必需的更为复杂。 我们可以提供取消渠道作为参数,而不是让timerStart在取消映射中查找它的索引:
func testTimer(i int, cancel <-chan interface{}) {
并具有创建频道的主要功能。 然后,您将可以从testTimer中删除映射访问,mutexLocker锁定和for循环。 如果出于此处未显示的目的仍需要映射,则可以在传递给testTimer的映射中放置相同的通道,如果没有,则也可以删除所有该代码。
这一切最终看起来像https://play.golang.org/p/iQUvc52B6Nk
希望有帮助helps
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.