dtmsaqtly798322992 2018-06-28 09:55
浏览 64
已采纳

如何停止另一个go-routine中正在监听的time.Timer?

I have an idle timeout timer being selected on in a goroutine, if I see activity I want to cancel the timer.

I had a look at the documentation and I'm not positive I'm clear on what it says.

func (t *Timer) Stop() bool
Stop prevents the Timer from firing. It returns true if the call stops the timer, false if the timer has already expired or been stopped. Stop does not close the channel, to prevent a read from the channel succeeding incorrectly.

To prevent a timer created with NewTimer from firing after a call to Stop, check the return value and drain the channel. For example, assuming the program has not received from t.C already:

if !t.Stop() { <-t.C }

This cannot be done concurrent to other receives from the Timer's channel.

I'm trying to understand when I have to drain the channel manually.

I'll list my understanding and if I'm wrong please correct me.

If Stop returned false this means either:

  • The timer was already stopped
    • In this case, reading from the channel will block so I shouldn't do it
  • The timer was already expired
    • Since I have another goroutine listening on the channel, can I know for certain if it has received the event?
    • From the "this cannot be done concurrent to other receives" part of the documentation it appears that this isn't an option, so what should I do?

In my case getting a superfluous event from the timer is no big deal, does that inform what I should do here?

  • 写回答

1条回答 默认 最新

  • douhuan1257 2018-06-28 10:57
    关注

    The reason that you might need to drain the channel is because of how goroutines are scheduled.

    Problem

    Imagine this case:

    1. Created a timer
    2. Timer fires, sending a value to t.C
    3. Nothing has received / read the value yet, but t.Stop() is called.

    In this case there is a value on the channel t.C, and t.Stop() returns false because "the timer already expired" (i.e. when it sent the value on t.C).

    The reason that the docs say "this cannot be done concurrent to other receives" is because there's not guarantee of ordering between the if !t.Stop { and the <-t.C. The stop command could return false, entering the if body. And then another goroutine could be scheduled and read the value from t.C that the body of the if statement was trying to drain. This would cause a datarace and result in blocking inside the if statement. (as you pointed out in your question!)

    Solution

    It depends on what the behaviour of the thing listening to the timer is.

    If you are just in a simple select:

    select {
        case result <- doWork():
        case <-t.C
    }
    

    Something like above. One of a few things could happen:

    1. The work could be done from doWork, sending the result, all good.
    2. The timer could fire, causing the timeout, breaking the select.
    3. Call to stop, stop the timer, only way out of the select is doWork() completing.
    4. The timer could fire, another routine calls t.Stop(), but it's too late because the value has been sent, causing the timeout, breaking the select.

    As long as you are OK with case 4, you do not need to interact / drain the channel after calling Stop.

    If you are not OK with case 4. You still cannot drain the t.C channel because there's another goroutine listening to it. This could block in the if statement. Instead you must find another way of laying out the code, or ensuring that your goroutine in the select is not still listening on the channel.

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

报告相同问题?

悬赏问题

  • ¥15 arduino控制ps2手柄一直报错
  • ¥15 使用rabbitMQ 消息队列作为url源进行多线程爬取时,总有几个url没有处理的问题。
  • ¥85 maple软件,solve求反函数,出现rootof怎么办?
  • ¥15 求chat4.0解答一道线性规划题,用lingo编程运行,第一问要求写出数学模型和lingo语言编程模型,第二问第三问解答就行,我的ddl要到了谁来求了
  • ¥15 Ubuntu在安装序列比对软件STAR时出现报错如何解决
  • ¥50 树莓派安卓APK系统签名
  • ¥65 汇编语言除法溢出问题
  • ¥15 Visual Studio问题
  • ¥20 求一个html代码,有偿
  • ¥100 关于使用MATLAB中copularnd函数的问题