0. Prefácio
Ao realizar programação simultânea, às vezes são necessárias funções de temporização, como monitorar se um determinado processo GO será executado por muito tempo, imprimir logs em intervalos regulares, etc.
Existem dois tipos principais de temporizadores na biblioteca padrão GO, um é o temporizador e o outro é o temporizador Ticker. Depois que o Timer é usado uma vez, ele se torna inválido e requer que Reset() entre em vigor novamente. O temporizador Ticker estará sempre em vigor. A seguir apresentaremos os dois tipos respectivamente.
1. Temporizador
Primeiro, vamos apresentar o princípio de implementação do temporizador GO .
Em um processo GO, todos os timers nele contidos são timerproc()
protegidos por uma goroutine executando uma função. Ele usa o algoritmo de heap de tempo (heap mínimo) para proteger todos os temporizadores. Sua estrutura de dados subjacente é baseada no heap mínimo da matriz. O elemento no topo do heap é o temporizador com o tempo limite de intervalo mais próximo. Esta goroutine irá despertar up regularmente e lê o topo do heap.Timer, executa a f
função ou sendtime()
funções correspondentes (essas duas funções serão apresentadas abaixo) e, em seguida, remove-o do topo do heap.
A seguir, observe a estrutura do Timer :
type Timer struct {
C <-chan Time
// contains filtered or unexported fields
}
Há apenas um canal exposto ao mundo exterior no Timer, e esse canal também é o núcleo do timer. Quando o temporizador terminar, o Timer enviará o valor para o canal. Quando o ambiente externo receber o valor neste canal, significa que o timer expirou. Ele pode ser usado com select para executar alguma lógica de timeout. Pode ser criado por time.NewTimer
ou a partir de um temporizador.time.AfterFunc
time.Afte
1.1 time.NewTimer() e time.After()
1.1.1 tempo.NewTimer()
Confira o código simples do aplicativo abaixo:
package main
import (
"fmt"
"time"
)
type H struct {
t *time.Timer
}
func main() {
fmt.Println("main")
h:=H{t: time.NewTimer(1*time.Second)}
go h.timer()
time.Sleep(10*time.Second)
}
func (h *H) timer() {
for {
select {
case <-h.t.C:
fmt.Println("timer")
}
}
}
Criamos um cronômetro e definimos o tempo para 1S. Em seguida, use um select para aceitar o C do temporizador. O processo GO executa a função timer() e irá bloquear até que ocorra o timeout (os dados de C são recebidos), momento em que o temporizador é impresso.
Parar() Parar temporizador
func (t *Timer) Stop() bool
Stop()
É um método de Timer. Chamar Stop()
o método irá interromper o tempo deste Timer, torná-lo inválido e então disparar um evento cronometrado.
Na verdade, após chamar este método, este Timer será removido do time heap.
Reset() redefine o temporizador
Observe que o temporizador não será executado novamente após expirar uma vez, então você precisa chamar a função Reset para redefini-lo . Modifique o código em select e adicione um código de reset no Case:
select {
case <-h.t.C:
fmt.Println("timer")
h.t.Reset(1*time.Second)
}
Como você pode ver, o cronômetro será impresso continuamente, pois a função Reset é usada para zerar o cronômetro.
Perceber! O método Reset não pode ser chamado à vontade . O documento do site oficial enfatiza especificamente:
Para um temporizador criado com NewTimer, Reset deve ser invocado apenas em temporizadores parados ou expirados com canais esgotados.
O significado geral é que, a menos que o temporizador tenha sido interrompido ou tenha expirado, não chame o método Reset, porque se o temporizador ainda não tiver expirado, não o interrompa primeiro, mas reinicie diretamente, então o temporizador antigo ainda existe e é ainda assim, pode ser acionado e algo inesperado pode acontecer. Portanto, o código a seguir é geralmente usado para redefinir com segurança um temporizador de status desconhecido (no código acima, quando Reset é chamado, ele está sempre em estado de tempo limite):
if !t.Stop() {
select {
case <-h.t.C:
default:
}
}
h.t.Reset(1*time.Second)
1.1.2 tempo.Depois()
Este método é como uma versão minimalista do Timer. Quando chamado time.After()
, ele retornará diretamente um canal. Quando atingir o tempo limite, este canal receberá um valor. Basta usá-lo da seguinte maneira:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("main")
go ticker()
time.Sleep(100 * time.Second)
}
func ticker() {
for {
select {
case <-time.After(1 * time.Second):
fmt.Println("timer")
}
}
}
Observe que embora esse método seja simples, não existe um método Reset para zerar o cronômetro, mas ele pode ser combinado com chamadas repetidas de for e select para simular uma redefinição.
1.1.3 função de horário de envio
NewTimer
Esses After
dois métodos de criação executarão Timer
uma função integrada na biblioteca padrão após o tempo limite: sendTime
enviar a hora atual para o canal.
1,2 vez.AfterFunc
Este método pode aceitar um parâmetro do tipo func. Após o tempo expirar, esta função será executada. Verifique o código a seguir. Adivinhe qual será o resultado?
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("main")
t := time.AfterFunc(1*time.Second, func() {
fmt.Println("timer")
})
go timer(t)
time.Sleep(10 * time.Second)
}
func timer(t *time.Timer) {
select {
case <-t.C:
fmt.Println("123")
}
}
Como resultado, apenas main e timer são impressos. Isso ocorre porque este método não chama a função sendtime() citada acima, ou seja, não envia o valor para o Canal do Timer, portanto o select sempre irá bloquear.
função f
AfterFunc
A soma acima é NewTimer
adicionada deliberadamente After
devido à existência da função f. Criada desta forma Timer
, após atingir o tempo limite , goroutine
a função f
será executada em um arquivo separado ao invés da função sendtime.
Observe que os parâmetros passados de fora f
não são executados diretamente no timerproc
método, mas um novo é iniciado goroutine
para executar este método.
2. Temporizador
Ticker
O temporizador pode acionar continuamente eventos de tempo periodicamente sem exigir operações adicionais de reinicialização.
Seu uso é semelhante ao do Timer. Ao time.NewTicker
criar um Ticker, basta utilizá-lo da seguinte forma:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("main")
t:=time.NewTicker(1*time.Second)
go timer(t)
time.Sleep(10 * time.Second)
}
func timer(t *time.Ticker) {
for{
select {
case <-t.C:
fmt.Println("timer")
}
}
}