我使用股票行情器定期执行任务,但是更改它时遇到了一些问题。收到一些消息后,我将股票代码更改为新的股票,并更改时间间隔。这是将重现此问题的示例代码:

package main

import (
    "fmt"
    "time"
)

type A struct {
    ticker *time.Ticker
}

func (a *A) modify() {
    a.ticker.Stop()
    a.ticker = time.NewTicker(time.Second)
}
func main() {
    a := new(A)
    a.ticker = time.NewTicker(time.Second)
    go func() {
        for {
            select {
            case <-a.ticker.C:
                fmt.Println("now")
                go a.modify()
                /*
                    default:
                        //fmt.Println("default")
                        time.Sleep(time.Millisecond * 100)
                */
            }
        }
    }()
    time.Sleep(time.Second * 60)
}

“现在”仅打印一次。但是,如果我删除“go”,它将连续打印,如下所示:
package main

import (
    "fmt"
    "time"
)

type A struct {
    ticker *time.Ticker
}

func (a *A) modify() {
    a.ticker.Stop()
    a.ticker = time.NewTicker(time.Second)
}
func main() {
    a := new(A)
    a.ticker = time.NewTicker(time.Second)
    go func() {
        for {
            select {
            case <-a.ticker.C:
                fmt.Println("now")
                a.modify()
                /*
                    default:
                        //fmt.Println("default")
                        time.Sleep(time.Millisecond * 100)
                */
            }
        }
    }()
    time.Sleep(time.Second * 60)
}

另外,如果我不注释默认子句,则可以连续打印“now”。
谁能解释这将如何发生?

最佳答案

问题是goroutine异步运行。
使用a.modify()时,您的代码的行为如下:

  • a.ticker.C滴答作响
  • 停止旧的代码,并创建新的a.ticker.C
  • 使用a.ticker.C等待select

  • 在这种情况下,在2中新创建的a.ticker.C与在3中等待的频道相同。

    如果在goroutine中执行2.。可以按以下顺序完成
  • a.ticker.C滴答作响
  • 使用a.ticker.C等待select
  • 停止旧的代码,并创建新的a.ticker.C

  • 在这种情况下,在2.中等待的频道不同于在3.中新创建的频道。
    由于选择通道是一个已停止的旧通道,因此永远不会出现任何滴答声。

    您可以通过插入一些fmt.Printf来确认此行为,并注意a.ticker.C的地址。
    func (a *A) modify() {
        a.ticker.Stop()
        fmt.Printf("ticker stopped: %p\n", &a.ticker.C)
        a.ticker = time.NewTicker(time.Second)
        fmt.Printf("new ticker created: %p\n", &a.ticker.C)
    }
    func main() {
        a := new(A)
        a.ticker = time.NewTicker(time.Second)
        go func() {
            for {
                fmt.Printf("waiting for ticker: %p\n", &a.ticker.C)
                select {
            ....
    

    a.modify():
    waiting for ticker: 0xc420010100
    ticker stopped: 0xc420010100
    new ticker created: 0xc420068000
    waiting for ticker: 0xc420068000
    ticker stopped: 0xc420068000
    new ticker created: 0xc420068080
    waiting for ticker: 0xc420068080
    

    go a.modify():
    waiting for ticker: 0xc420010100
    waiting for ticker: 0xc420010100
    ticker stopped: 0xc420010100
    new ticker created: 0xc420066040
    

    您会发现,使用go a.modify()并不需要等待新创建的频道。

    更新默认行为:

    当将default:go a.modify()一起使用时,其行为将像这样。
  • 等待a.ticker.C,收到勾号,然后调用go a.modify()来执行。3.
  • 等待a.ticker.C,什么都没有,所以回退到默认值并休眠100ms。
  • 停止旧行情自动收录器,更新a.ticker.C
  • 等待a.ticker.C,什么都没有,所以回退到默认值并休眠100ms。
  • 等待a.ticker.C,什么也没得到,所以回退到默认值并休眠100ms。
  • 等待a.ticker.C,什么都没有,所以回退到默认值并休眠100ms。

  • .....
  • 等待a.ticker.C,打勾,调用go a.modify()

  • 关键是,即使您没有从for{}中获取任何信息,a.ticker.C循环也可以继续进行。
    您可以使用相同的代码确认行为。
    waiting ticker: 0xc420010100     <-- 1.
    now                              <-- 1.
    waiting ticker: 0xc420010100     <-- 2.
    default                          <-- 2.
    ticker stopped: 0xc420010100     <-- 3.
    new ticker created: 0xc420066240 <-- 3.
    waiting ticker: 0xc420066240     <-- 4.
    default                          <-- 4.
    

    关于go - 在select {case:channel}中更改 channel ,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41629227/

    10-12 01:37