dongpang1898 2019-09-07 05:16
浏览 167
已采纳

Golang具有map + channel + mutex的多个计时器

So I'm implementing multiple timers using map/channel/mutex. In order for timer to cancel, I have a channel map that stores cancel info, below is the code:

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))
        }
    }
}

Now this one gives me deadlock, if I remove all mutex.lock/unlock, it gives me concurrent map read and map write. So what am I doing wrong?

I know sync.Map solves my problem, but the performance suffers significantly, so I kinda wanna stick with the map solution.

Thanks in advance!

  • 写回答

1条回答 默认 最新

  • duanjiaopi8218 2019-09-07 06:14
    关注

    There's a few things going on here which are going to cause problems with your script:

    cancelTimer creates a channel make(chan interface{}) which has no buffer, e.g. make(chan struct{}, 1). This means that sending to the channel will block until another goroutine attempts to receive from that same channel. So when you attempt to call cancelTimer from the main goroutine, it locks mutexLocker and then blocks on sending the cancellation, meanwhile no other goroutine can lock mutexLocker to receive from the cancellation channel, thus causing a deadlock.

    After adding a buffer, the cancelTimer call will return immediately.

    We will then run into a few other little issues. The first is that the program will immediately quit without printing anything. This happens because after launching the test goroutines and sending the cancel, the main thread has done all of its work, which tells the program it is finished. So we need to tell the main thread to wait for the goroutines, which sync.WaitGroup is very good for:

    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()
    }
    

    I can see you've added the mutexLocker to protect the map and later added the for loop to give each goroutine an opportunity to acquire mutexLocker to check their timers. This results in a lot of work for the computer, and more complicated code than is necessary. Instead of having timerStart look up it's index in the cancellations map, we can provide the cancellation channel as an argument:

    func testTimer(i int, cancel <-chan interface{}) {
    

    and have the main function create the channels. You will then be a le to remove map access, mutexLocker locking, and the for loop from testTimer. If you still require the map for purposes not shown here, you can put the same channel in the map that you pass to testTimer, and if not you can remove all of that code too.

    This all ends up looking something like https://play.golang.org/p/iQUvc52B6Nk

    Hope that helps

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥20 iOS绕地区网络检测
  • ¥15 python验证码滑块图像识别
  • ¥15 根据背景及设计要求撰写设计报告
  • ¥15 QT6颜色选择对话框显示不完整
  • ¥20 能提供一下思路或者代码吗
  • ¥15 用twincat控制!
  • ¥15 请问一下这个运行结果是怎么来的
  • ¥15 单通道放大电路的工作原理
  • ¥30 YOLO检测微调结果p为1
  • ¥15 DS18B20内部ADC模数转换器