El principio de implementación interna del canal de comunicación Go Coroutine

El modelo de programación concurrente de la mayoría de los lenguajes de programación se basa en la sincronización de hilos y memoria, mientras que el modelo de programación concurrente de Golang usa gorutinas y canales en su lugar.Las grutinas se usan para realizar tareas concurrentes y los canales se usan para control de concurrencia y comunicación gorutina. Esta vez sigue una demostración para explorar el misterio del fondo del canal.

estructura de datos del canal

type hchan struct {
    
    
   // chan里元素数量
   qcount   uint
   // chan维护的数组的长度
   dataqsiz uint
   // 维护的数组的指针
   buf      unsafe.Pointer
   // chan中元素大小
   elemsize uint16
   // chan是否被关闭的标志
   closed   uint32
   // chan 中元素类型
   elemtype *_type
   // 已发送元素在循环数组中的索引
   sendx    uint
   // 已接收元素在循环数组中的索引
   recvx    uint
   // 等待接收的goroutine队列
   recvq    waitq
   // 等待发送的goroutine队列
   sendq    waitq
   // 保证对chan的读写是原子操作
   lock mutex
}

Puede resumirse en la siguiente figura para facilitar su comprensión.
Inserte la descripción de la imagen aquí

Código de muestra

/*
示例说明:channel容量为3,设置六个发送,构造goroutine被阻塞状态,
八个接收,构造接收的G阻塞状态场景,便于探秘channel运作过程。
*/
func main() {
    
    

   ch := make(chan int, 3)
   CheckChannel(ch)
}

func CheckChannel(ch chan int) {
    
    
   //6个发送
   go func() {
    
    ch <- 1}()
   go func() {
    
    ch <- 2}()
   go func() {
    
    ch <- 3}()
   go func() {
    
    ch <- 4}()
   go func() {
    
    ch <- 5}()
   go func() {
    
    ch <- 6}()
   //8个接收
   go func() {
    
    <- ch}()
   go func() {
    
    <- ch}()
   go func() {
    
    <- ch}()
   go func() {
    
    <- ch}()
   go func() {
    
    <- ch}()
   go func() {
    
    <- ch}()
   go func() {
    
    <- ch}()
   go func() {
    
    <- ch}()

   time.Sleep(time.Second * 5)
   fmt.Println("stop")
}

Análisis de la etapa de depuración

Simplemente almacena el canal en búfer para depurar el código.
Primer tipo de búfer: cuando se ejecuta CheckChannel, se ha inicializado una parte de la memoria.
Inserte la descripción de la imagen aquí

Abra la interfaz dehug para ver la estructura de datos:

Inserte la descripción de la imagen aquí
Recién inicializado en este momento, el número de elementos en la matriz inferior es 0. Tanto el índice de envío como el índice de recepción apuntan al índice de matriz 0, y no hay G en las colas de envío y recepción.

1. Envía un elemento

Siguiente paso, después de ejecutar la primera G, observe la depuración y Inserte la descripción de la imagen aquí
observe el contenido del cuadro rojo, y vea que los primeros datos de groutine se reciben en la posición 0 de la matriz inferior, y el puntero sendx inferior apunta al índice 1 de la array, lo que indica que es hora de enviar de nuevo Los datos serán recibidos por el índice de matriz 1, recvx es 0, lo que indica que cuando haya G recibiendo datos, se recibirán los datos en el índice de matriz 0. La estructura es la siguiente:
Inserte la descripción de la imagen aquí

2. Envío de bloqueo

Continúe con el paso, hasta el final del envío de datos, verifique debug:
Inserte la descripción de la imagen aquí
puede ver que debido a que no hay receptor, la matriz inferior está llena. Verifique que los valores de sendx y recvx sean ambos 0, lo que indica que si hay un receptor, saque los datos del índice 0. Si hay un remitente, los datos se copiarán a 0 (si se eliminan los datos de 0).

Además, verifique sendq, puede ver que hay una acumulación de colas G en el interior. Esto se debe a que la matriz subyacente está llena. El canal creará una estructura de datos sudog, obtendrá el puntero de G y colocará G en su propia cola de espera . En este momento, la acumulación de G En el estado Gwaiting, no está ni en la cola de ejecución global ni en la cola de ejecución de un determinado P (planificador), esperando que un receptor reciba datos y activando la función goready para hacer el G entra en el estado Grunnable programable.

Inserte la descripción de la imagen aquí
Se puede ver que se presionan tres G en el área de la estructura, y la siguiente G se conecta a través de la siguiente. Por supuesto, también hay punteros previos conectados a la G anterior, y el puntero a la primera o última G apunta a cero. La estructura es la siguiente:

Inserte la descripción de la imagen aquí

3. Recibe el primer elemento

Continúe pasando al primer G recibido y observe la estructura del canal:

Inserte la descripción de la imagen aquí
Se puede ver que se ha recibido el 1 en el índice 0. Debido al receptor, el G en la cola de espera se despierta y entra en el estado programable, es decir, el estado Grunnable, y se libera después de que se completa la programación. El segundo G del backlog en la cola sendq es el encabezado de la cola, y sendx y recvx apuntan al índice 1. El canal de envío y recepción de datos se realizará en el índice de matriz 1.

Verifique la cola de G
Inserte la descripción de la imagen aquí
en sendq : puede ver que la lechada al principio de la cola se ha liberado y solo quedan dos G en la cola;
Inserte la descripción de la imagen aquí

4. Finaliza el primer lote de recepción

Continúe pasando hasta que se ejecute la tercera recepción G:
Inserte la descripción de la imagen aquí
puede ver que todos los elementos de la matriz se han convertido en los elementos que enviará G que está atrasada en sendq por primera vez, y desde que se envió, todas las demoras de G. Todos los punteros de índice apuntan a 0.
Inserte la descripción de la imagen aquí

5. Hay lugares libres en la matriz.

Continúe pasando y observe las posiciones del puntero de sendx y recvx:
Inserte la descripción de la imagen aquí
puede ver que debido a que no hay remitente, el puntero de sendx apunta al índice 0, y recvx se mueve hacia atrás para asignar valores a los elementos restantes del arreglo. no hay acumulación de G en este momento., Un receptor libera un bit de índice.

La estructura de datos en este momento:
Inserte la descripción de la imagen aquí

6. La matriz no tiene elementos.

Continúe pasando hasta el final de la sexta recepción G. El
Inserte la descripción de la imagen aquí
efecto original se restaura, sin elementos y sin acumulación de G.
En este momento, la estructura de datos es la siguiente:

Inserte la descripción de la imagen aquí

7. Recibir bloqueo

Continúe dando un paso para crear la recepción G, hasta el final de la creación de G, observe:
Inserte la descripción de la imagen aquí

Dos atrasos de G en la cola de recepción recvq se bloquean y caen en el estado de espera G. Dado que no habrá ningún remitente detrás del programa, se bloqueará hasta que salga la corrutina principal.

Estructura de datos en este momento:
Inserte la descripción de la imagen aquí

8 Fin de la puesta en servicio

Continúe ejecutando, la corrutina principal duerme durante cinco segundos, sale y todas las subcorutinas salen.

Para los canales sin búfer, el valor se copia directamente de la G enviada a la G.

Resumen de depuración

Después de todo, pasar un mensaje a través de un canal es una copia del valor. El canal almacenado en búfer primero copia el valor del remitente G en la matriz que mantiene, y luego lo copia en el receptor G, mientras que el tipo sin búfer copia el datos directamente desde la pila de envío al receptor.

Última publicación de una imagen
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/QiuHaoqian/article/details/108999754
Recomendado
Clasificación