"Menos de un segundo" ¿Entiendes goroutine y canal?

biblioteca de código abierto "van a casa" centrándose Go tecnología del lenguaje de pila y preguntas de la entrevista a la ayuda de Gopher se subió a un escenario más grande, la bienvenida go home~

Introducción de antecedentes

Todo el mundo sabe que un proceso es la unidad básica de asignación de recursos del sistema operativo. Tiene un espacio de memoria independiente. Los subprocesos pueden compartir el espacio de memoria del mismo proceso. Por lo tanto, los subprocesos son relativamente livianos y la sobrecarga de cambio de contexto también es pequeña. Aunque el hilo ya es liviano, todavía ocupa casi 1M de memoria. El Goroutine, conocido como "hilo liviano" presentado hoy, puede ser tan pequeño como decenas de K o incluso unos pocos K, y la sobrecarga de conmutación es menor.

Además, en la programación tradicional de Socket, debe mantener un grupo de subprocesos para asignar subprocesos para cada paquete de envío y recepción de Socket, y debe establecer una correspondencia entre la CPU y el número de subprocesos para garantizar que cada tarea pueda asignarse a la CPU en El programa Go puede distribuir inteligentemente las tareas en la rutina a la CPU.

Como usar

Ahora asumimos un escenario en el que usted es un gerente de la compañía que pasa dos horas al día procesando correo y seis horas de reunión, entonces el programa puede escribirse así.

func main() {

	time.Sleep(time.Hour * 2) //处理邮件
	time.Sleep(time.Hour * 6) //开会

	fmt.Println("工作完成了")
}

Ejecutarlo

archivo

Efectivamente, lleva 8 horas completar el trabajo, entonces, ¿cómo simplificar el trabajo? Así es, por favor pídale ayuda a una hermana asistente, déjela manejar el correo, de modo que solo necesite reunirse durante 6 horas.

archivo

Comencemos a escribir código, definamos primero una función de asistente.

func assistant() {
	time.Sleep(time.Hour * 2)
}

Luego goinstálelo con un comando de artefacto en la función principal , de modo que el asistente tarde mucho tiempo y ya no le tome más tiempo.

func main() {

	go assistant()

	time.Sleep(time.Hour * 6) //开会

	fmt.Println("工作完成了")
}

Ejecutarlo

archivo

Realmente tomó solo seis horas terminar el trabajo. Estimados jueces, ¿ven? No, esta es una rutina, simplemente goagregue el nombre de la función al comando, es así de simple.

archivo

Pero sé que no se contentará con el status quo.

Función anónima

Además, dado que goroutine admite funciones ordinarias, por supuesto, también admite funciones anónimas.

go func() {
  time.Sleep(time.Hour * 2)
}()

Cómo comunicarse entre corutinas

Aunque podemos crear fácilmente un montón de corutinas, las corutinas que no pueden comunicarse no tienen alma. Si el asistente te está ayudando a lidiar con el correo y de repente quieres pedirle que tome té con leche, ¿quieres notificarle?

archivo

¿Cómo lo notificas? Esto lleva al famoso canal, la traducción al chino de "canal", como su nombre lo indica, su función es establecer un canal entre las rutinas, un extremo puede transmitir continuamente fuentes de datos al otro extremo del canal.

archivo

El método de declaración también es muy simple, solo hazlo. Tome el siguiente código como ejemplo, representa la inicialización de una variable de tipo de canal y solo se pueden almacenar datos de tipo de cadena en el canal.

ch := make(chan string)

Una vez completada la inicialización, para establecer una conexión con la función de rutina, primero debe pasar la variable chan a la función de rutina.

go assistant(ch)

Por supuesto, la función de rutina necesita poder recibir chan. Vamos a profundizar en la función y ver qué hemos hecho.

func assistant(ch chan string) {

	go func() {
		for {
			fmt.Println("看了一封邮件")
			time.Sleep(time.Second * 1)
		}
	}()

	msg := <-ch
	if msg == "喝奶茶去呗" {
		ch <- "好啊"
	}
}

Se ha creado una rutina dentro de la función para tratar los correos, mientras se espera la notificación del jefe. Con atención, debe ver cómo obtener los datos del canal. Sí, solo necesita agregar el <-símbolo frente a la variable del canal para obtener el valor. De manera similar, agregar el símbolo en la parte posterior es conectar los datos del canal.

ch <- "pingyeaa"
<- ch

archivo

Si el canal no tiene datos, el consumidor bloqueará hasta que haya datos. Por supuesto, el compilador es muy inteligente. Si encuentra que no hay lugar para insertar datos en el canal durante la compilación, entrará en pánico y provocará un punto muerto.

fatal error: all goroutines are asleep - deadlock!

Continúe mirando el código, lo que significa aproximadamente que si el jefe envía "Beber té con leche para cantar", devuelve "OK". Como el canal no tiene datos al principio, la rutina se bloqueará hasta que la función principal escriba en el canal En las noticias.

Ahora veamos la lógica de implementación de la función principal. El canal de declaración y las variables del canal entrante no se repetirán. Solo necesitamos esperar 5 segundos y escribir el mensaje del té con leche en el canal. Debido a que la rutina del asistente acaba de escribir un mensaje "bueno" a ch después de recibir el mensaje, la función principal debe leer el mensaje del asistente después de enviar la solicitud.

ch := make(chan string)

go assistant(ch)

time.Sleep(time.Second * 5)
ch <- "喝奶茶去呗"

resp := <-ch
fmt.Println(resp)

Del mismo modo, la función principal <-chse bloqueará hasta que el asistente responda al mensaje. Hay otros dos puntos a tener en cuenta: en primer lugar, si la función principal se ejecuta antes de la goroutina, la goroutina también se destruirá; en segundo lugar, la principal también es una goroutina.

Finalmente, para cerrar el canal, de hecho, no es necesario cerrar el canal. Es diferente del archivo. Si la gorita no utiliza ningún canal, se destruirá automáticamente. El propósito del cierre es notificar al otro extremo del canal que no se enviarán más mensajes. El otro extremo puede Use el segundo parámetro de <-ch para obtener el estado de cierre del canal.

close(ch)

data, ok := <-ch

Selección de canales multiplexados

El <-ch en el ejemplo anterior solo puede leer un mensaje del canal. Si hay más de un mensaje en el canal, ¿cómo leerlo?

archivo

Muchos estudiantes deberían pensar en la travesía tanto como yo. Así es, la travesía puede obtener datos del canal.

for {
  fmt.Println(<-ch)
}

También se puede atravesar de esta manera.

for d := range ch {
  fmt.Println(d)
}

Pero, ¿qué sucede si necesita recibir datos de múltiples canales simultáneamente? ¿Recibir dos variables de canal en el bucle?

for {
  data, ok := <-ch1
  data, ok := <-ch2
}

Aunque los datos se pueden extraer de esta manera, el rendimiento es deficiente. La palabra clave de selección proporcionada por nosotros se utiliza específicamente para resolver el problema de la lectura de datos multicanal. La sintaxis es muy similar a la del interruptor. Select distribuirá los datos de múltiples canales a diferentes lógicas de procesamiento.

func main() {

	ch1 := make(chan int)
	ch2 := make(chan int)

	go func() {
		for {
			select {
			case d := <-ch1:
				fmt.Println("ch1", d)
			case d := <-ch2:
				fmt.Println("ch2", d)
			}
		}
	}()

	ch1 <- 1
	ch1 <- 2
	ch2 <- 2
	ch1 <- 3
}

Tiempo de espera de simulación

Además, en algunos casos, no queremos que el canal se bloquee por demasiado tiempo. Suponiendo que los datos del canal no se puedan recuperar en 5 segundos, salimos con un tiempo de espera. Luego podemos usar el tiempo. Después del método para lograrlo. Después de devolver un tipo de canal, su función es pasar un tiempo objetivo (como 5 segundos), podemos obtener la notificación de tiempo de espera predeterminado a través del canal después de 5 segundos, para lograr el propósito del temporizador.

func main() {

	ch1 := make(chan int)
	ch2 := make(chan int)

	go func() {
		for {
			select {
			case d := <-ch1:
				fmt.Println("ch1", d)
			case d := <-ch2:
				fmt.Println("ch2", d)
			case <-time.After(time.Second * 5):
				fmt.Println("接收超时")
			}
		}
	}()

	time.Sleep(time.Second * 6)
}

Canal cerrado lectura extendida

El pánico se activará si el canal se cierra nuevamente

ch := make(chan int)
close(ch)
ch <- 1
panic: send on closed channel

Longitud de ajuste del canal

Puede establecer la longitud del canal mediante el método make. Como un búfer, el lado del productor se bloqueará cuando el canal esté lleno y el lado del consumidor se bloqueará cuando el canal esté vacío.

ch := make(chan int, 3)

ch <- 1
ch <- 2
ch <- 2
ch <- 2

fmt.Println(len(ch))

Los canales cerrados aún pueden leer datos

ch := make(chan int, 3)

ch <- 1
ch <- 2
ch <- 2

close(ch)

for d := range ch {
  fmt.Println(d)
}

Gracias por mirar. Si cree que el artículo es útil para usted, bienvenido a prestar atención a la cuenta pública "Pingya" y centrarse en el lenguaje Go y los principios técnicos.
Sígueme

Supongo que te gusta

Origin www.cnblogs.com/pingyeaa/p/12699543.html
Recomendado
Clasificación