Princípio e uso do temporizador GO

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 ffunçã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.NewTimerou a partir de um temporizador.time.AfterFunctime.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

NewTimerEsses Afterdois métodos de criação executarão Timeruma função integrada na biblioteca padrão após o tempo limite: sendTimeenviar 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

AfterFuncA soma acima é NewTimeradicionada deliberadamente Afterdevido à existência da função f. Criada desta forma Timer, após atingir o tempo limite , goroutinea função fserá executada em um arquivo separado ao invés da função sendtime.

Observe que os parâmetros passados ​​de fora fnão são executados diretamente no timerprocmétodo, mas um novo é iniciado goroutinepara executar este método.

2. Temporizador

TickerO temporizador pode acionar continuamente eventos de tempo periodicamente sem exigir operações adicionais de reinicialização.

Seu uso é semelhante ao do Timer. Ao time.NewTickercriar 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")
      }
   }
}

Acho que você gosta

Origin blog.csdn.net/doreen211/article/details/125379493
Recomendado
Clasificación