gpt4 book ai didi

go - 在 select{case :channel} 中更改 channel

转载 作者:数据小太阳 更新时间:2023-10-29 03:26:01 24 4
gpt4 key购买 nike

我使用 Ticker 定期执行任务,但在更改它时遇到了一些问题。我会在收到一些消息时将自动收报机更改为新的自动收报机并更改间隔。下面是重现此问题的示例代码:

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() 时,您的代码表现如下:

  1. a.ticker.C获取报价
  2. 停止旧代码,并创建新的 a.ticker.C
  3. 使用select等待a.ticker.C

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

如果你在 goroutine 中执行 2.可以按以下顺序进行

  1. a.ticker.C获取报价
  2. 使用select等待a.ticker.C
  3. 停止旧代码,并创建新的 a.ticker.C

在这种情况下,2. 中等待的 channel 不同于 3. 中新创建的 channel 。由于选择 channel 是已停止的旧 channel ,因此它永远不会得到任何提示。

您可以通过插入一些 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() 您无需等待新创建的 channel 。

默认行为更新:

default:go a.modify() 一起使用时,它将表现如下。

  1. 等待 a.ticker.C,得到 tick,调用 go a.modify() 执行 3。
  2. 等待 a.ticker.C,什么也没得到,所以回退到默认值并休眠 100 毫秒。
  3. 停止旧代码,更新a.ticker.C
  4. 等待 a.ticker.C,什么也没得到,所以回退到默认值并休眠 100 毫秒。
  5. 等待 a.ticker.C,什么也没得到,所以回退到默认值并休眠 100 毫秒。
  6. 等待 a.ticker.C,什么也没得到,所以回退到默认值并休眠 100 毫秒。

.....

  1. 等待 a.ticker.C,得到 tick,调用 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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com