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.
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.
Abra la interfaz dehug para ver la estructura de datos:
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
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:
2. Envío de bloqueo
Continúe con el paso, hasta el final del envío de datos, verifique debug:
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.
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:
3. Recibe el primer elemento
Continúe pasando al primer G recibido y observe la estructura del canal:
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
en sendq : puede ver que la lechada al principio de la cola se ha liberado y solo quedan dos G en la cola;
4. Finaliza el primer lote de recepción
Continúe pasando hasta que se ejecute la tercera recepción G:
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.
5. Hay lugares libres en la matriz.
Continúe pasando y observe las posiciones del puntero de sendx y recvx:
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:
6. La matriz no tiene elementos.
Continúe pasando hasta el final de la sexta recepción G. El
efecto original se restaura, sin elementos y sin acumulación de G.
En este momento, la estructura de datos es la siguiente:
7. Recibir bloqueo
Continúe dando un paso para crear la recepción G, hasta el final de la creación de G, observe:
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:
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