Principio y uso del temporizador GO

0. Prefacio

Al realizar programación concurrente, a veces se necesitan funciones de sincronización, como monitorear si un determinado proceso GO se ejecutará durante demasiado tiempo, imprimir registros a intervalos regulares, etc.

Hay dos tipos principales de temporizadores en la biblioteca estándar GO, uno es el temporizador y el otro es el temporizador Ticker. Después de que el temporizador se usa una vez, deja de ser válido y requiere que Reset() vuelva a tener efecto. El temporizador Ticker siempre estará vigente, a continuación presentaremos los dos tipos respectivamente.

1. Temporizador

Primero, introduzcamos el principio de implementación del temporizador GO .
En un proceso GO, todos los temporizadores que contiene están timerproc()protegidos por una rutina que ejecuta una función. Utiliza el algoritmo de montón de tiempo (montón mínimo) para proteger todos los temporizadores. Su estructura de datos subyacente se basa en el montón mínimo de la matriz. El elemento en la parte superior del montón es el temporizador con el tiempo de espera de intervalo más cercano. Esta rutina se activará Se activa regularmente y lee el temporizador en la parte superior del montón, ejecuta la ffunción o sendtime()funciones correspondientes (estas dos funciones se presentarán a continuación) y luego las elimina de la parte superior del montón.

A continuación, observe la estructura de Timer :

type Timer struct {
    C <-chan Time
    // contains filtered or unexported fields
}

Solo hay un canal expuesto al mundo exterior en Timer, y este canal también es el núcleo del temporizador. Cuando finaliza el temporizador, el temporizador enviará el valor al canal. Cuando el entorno externo recibe el valor en este canal, significa que el temporizador ha agotado el tiempo de espera. Se puede utilizar con select para ejecutar alguna lógica de tiempo de espera. Puede ser creado por time.NewTimero time.AfterFuncdesde un temporizador.time.Afte

1.1 time.NewTimer() y time.After()

1.1.1 tiempo.NewTimer()

Consulte el código de aplicación simple a continuación:

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")
      }
   }

}

Creamos un temporizador y configuramos el tiempo en 1S. Luego use una selección para aceptar el C del temporizador. El proceso GO ejecuta la función timer() y se bloqueará hasta que se agote el tiempo de espera (se reciben los datos de C), momento en el cual se imprime el temporizador.

Detener() Detener temporizador

func (t *Timer) Stop() bool

Stop()Es un método de Temporizador. Llamar Stop()al método detendrá el tiempo de este Temporizador, lo invalidará y luego activará un evento cronometrado.

De hecho, después de llamar a este método, este temporizador se eliminará del montón de tiempo.

Reset() reinicia el temporizador

Tenga en cuenta que el temporizador no se ejecutará nuevamente después de que se agote una vez, por lo que debe llamar a la función Restablecer para restablecerlo . Modifique el código en seleccionar y agregue un código de reinicio en Caso:

select {
case <-h.t.C:
   fmt.Println("timer")
   h.t.Reset(1*time.Second)
}

Como puede ver, el temporizador se imprimirá continuamente, esto se debe a que la función Restablecer se utiliza para restablecer el temporizador.

¡Aviso! El método Reset no se puede llamar a voluntad.El documento del sitio web oficial enfatiza específicamente:

Para un temporizador creado con NewTimer, el reinicio debe invocarse solo en temporizadores detenidos o vencidos con canales agotados.

El significado general es que, a menos que el temporizador se haya detenido o se haya agotado el tiempo de espera, no llame al método Reset, porque si el temporizador aún no se ha agotado, no lo detenga primero, sino que lo reinicie directamente, entonces el temporizador anterior todavía existe y es Aún así, puede activarse y puede suceder algo inesperado. Por lo tanto, el siguiente código generalmente se usa para restablecer de forma segura un temporizador de estado desconocido (en el código anterior, cuando se llama a Restablecer, siempre está en un estado de tiempo de espera):

if !t.Stop() {
    select {
    case <-h.t.C: 
    default:
    }
}
h.t.Reset(1*time.Second)

1.1.2 tiempo.Después()

Este método es como una versión minimalista de Timer. Cuando se llama time.After(), devolverá directamente un canal. Cuando se agote el tiempo, este canal recibirá un valor. Simplemente utilícelo de la siguiente manera:

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")
		}
	}
}

Tenga en cuenta que, aunque este método es simple, no existe un método de reinicio para restablecer el temporizador, pero se puede combinar con llamadas repetidas de for y select para simular un reinicio.

1.1.3 función de tiempo de envío

NewTimerEstos Afterdos métodos de creación ejecutarán Timeruna función incorporada en la biblioteca estándar después del tiempo de espera: sendTimeenviar la hora actual al canal.

1.2 tiempo.AfterFunc

Este método puede aceptar un parámetro de tipo func. Después de que expire el temporizador, esta función se ejecutará. Verifique el siguiente código. ¿Adivina cuál será el 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, sólo se imprimen principal y temporizador. Esto se debe a que este método no llama a la función sendtime() mencionada anteriormente, es decir, no envía el valor al Canal del Temporizador, por lo que el select siempre se bloqueará.

función f

AfterFuncLa suma anterior se NewTimeragrega deliberadamente Afterdebido a la existencia de la función f. Creada de esta manera Timer, una vez alcanzado el período de tiempo de espera , goroutinela función fse ejecutará en un archivo separado en lugar de la función sendtime.

Tenga en cuenta que los parámetros pasados ​​​​desde el exterior fno se ejecutan directamente en el timerprocmétodo, sino que se inicia uno nuevo goroutinepara ejecutar este método.

2. Temporizador

TickerEl temporizador puede activar continuamente eventos de tiempo periódicamente sin requerir operaciones de reinicio adicionales.

Su uso es similar al de Timer. Al time.NewTickercrear un Ticker, simplemente utilícelo de la siguiente manera:

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")
      }
   }
}

Supongo que te gusta

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