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