El canal en el idioma Go se utiliza para la comunicación de rutina

Un canal es una estructura de datos que se utiliza para transferir datos. El diseño garantiza que solo una goroutine pueda recibir o transferir datos al mismo tiempo. Tanto el envío como la recepción son operaciones atómicas y no se interrumpirán.

El canal en el idioma Go es un tipo especial. En cualquier momento, solo una goroutine puede acceder al canal para enviar y obtener datos. Las gorutinas pueden comunicarse a través de canales.

Los canales se pueden utilizar para sincronizar el funcionamiento y la comunicación entre dos gorutinas pasando un tipo de valor específico. El operador <- se utiliza para especificar la dirección del canal, enviando o recibiendo. Si no se especifica la dirección, es un canal bidireccional.

ch <- v    // 把 v 发送到通道 ch

v := <-ch  // 从 ch 接收数据,并把值赋给 v          

Declarar tipo de canal

El canal en sí necesita un tipo para ser modificado, al igual que el tipo de segmento necesita identificar el tipo de elemento. El tipo de elemento del canal es el tipo de datos transmitidos en su interior, declarado de la siguiente manera:

var canal variable tipo de canal chan

El valor nulo del tipo chan es nil y solo se puede usar después de la declaración con make.

Crear canal

Para declarar un canal, use la palabra clave chan. El canal debe crearse antes de su uso. El canal es un tipo de referencia y debe crearse con make:

Instancia de canal: = make (tipo de datos chan)

  • Tipo de datos: el tipo de elemento transmitido en el canal.
  • Instancia de canal: identificador de canal creado por make.

Ejemplo:

ch1 := make(chan int)                 // 创建一个整型类型的通道
ch2 := make(chan interface{
    
    })         // 创建一个空接口类型的通道, 可以存放任意格式

type Equip struct{
    
     /* 字段 */ }
ch2 := make(chan *Equip)             // 创建Equip指针类型的通道, 可以存放*Equip

gorutina

Go language admite la concurrencia, solo necesitamos abrir goroutine a través de la palabra clave go. Una goroutine es un hilo liviano y la programación de una goroutine es administrada por el tiempo de ejecución de Golang.

Go permite el uso de una sentencia go para iniciar un nuevo hilo de tiempo de ejecución, una goroutine, para ejecutar una función con una goroutine diferente recién creada. Todas las goroutines del mismo programa comparten el mismo espacio de direcciones.

P.ej:

go a(x, y, z)

Esto abre una nueva goroutine.

Ejemplo:

func say(s string) {
    
    
    for i := 0; i < 5; i++ {
    
    
            time.Sleep(100 * time.Millisecond)
            fmt.Println(s)
    }
}

func main() {
    
    
    go say("world")
    say("hello")
}

sync.waitGroup
espera a que se complete la ejecución de todas las goroutines y bloquea la ejecución del hilo principal hasta que se ejecuten todas las goroutines.

GOMAXPROCS
llama a runtime.GOMAXPROCS () para establecer el número máximo de núcleos de CPU que se pueden calcular en paralelo y devuelve el valor anterior.

pasillo

Si goroutine es una concurrencia de programas de lenguaje Go, entonces los canales son el mecanismo de comunicación entre ellos. Un canal es un mecanismo de comunicación que permite a una goroutine enviar información de valor a otra goroutine a través de él. Cada canal tiene un tipo especial, que es el tipo de datos que pueden enviar los canales. Un canal que puede enviar datos int generalmente se escribe como chan int.

Go language aboga por el uso de la comunicación en lugar de la memoria compartida.Cuando un recurso debe compartirse entre goroutines, el canal establece una tubería entre goroutines y proporciona un mecanismo para garantizar el intercambio de datos sincrónico.
Al declarar el canal, debe especificar el tipo de datos que se compartirán. Los valores o punteros de tipos incorporados, tipos con nombre, tipos de estructura y tipos de referencia se pueden compartir a través de canales.

El método de comunicación aquí es utilizar canales, como se muestra en la siguiente figura:

Inserte la descripción de la imagen aquí
Cuando hay mucha gente en lugares públicos como estaciones de metro, comedores, baños, etc., todos han desarrollado el hábito de hacer colas para evitar un uso ineficiente de recursos y procesos de intercambio provocados por aglomeraciones y saltos de colas.
Lo mismo ocurre con el código y los datos. Para competir por los datos, múltiples goroutines inevitablemente causarán ineficiencia en la ejecución. La forma de usar las colas es la más eficiente. Los canales tienen una estructura similar a una cola.

Propósito del canal

  • Utilice canales para sincronizar goroutines
  • Use canalizaciones asincrónicas para proteger recursos críticos (solo aquellos que obtienen el token pueden usarlo y devolver la canalización después de su uso)
  • Usa tuberías para enviar eventos

Canal de sincronización

Un canal con una longitud de búfer de 0 se denomina tubería síncrona y se puede utilizar para sincronizar dos rutinas.

  • La operación de envío se bloquea hasta que el extremo receptor está listo para recibir
  • La operación de recepción se bloquea hasta que el remitente esté listo para enviar

Canal asincrónico

Un canal con una longitud de búfer superior a 0 se denomina tubería asíncrona. El canal asíncrono es establecer un valor de búfer para el canal.

  • Cuando el búfer no está lleno, la operación de envío no se bloquea.
  • Antes de que se lea el búfer, la operación de recepción no se bloquea.

La palabra clave range se utiliza para recorrer los datos leídos, de forma similar a las matrices o porciones. El formato es el siguiente:

v, ok := <-ch

Si el canal no recibe datos, ok será falso, y luego el canal se puede cerrar usando la función close ().

Ejemplo:

    // 声明通道    
    ch := make(chan int)
	
    var ch1 chan int       // ch1是一个正常的channel,不是单向的
    var ch2 chan<- float64 // ch2是单向channel,只用于写float64数据
    var ch3 <-chan int     // ch3是单向channel,只用于读取int数据
	
	// 通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小
	
	ch := make(chan int, 2)
	
	// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
	// 而不用立刻需要去同步读取数据
	ch <- 1
	ch <- 2
	
	// 获取这两个数据
	fmt.Println(<-ch)
    fmt.Println(<-ch)

func fibonacci(n int, c chan int) {
    
    
    x, y := 0, 1
    for i := 0; i < n; i++ {
    
    
            c <- x
            x, y = y, x+y
    }
    close(c)
}


func main() {
    
    
    c := make(chan int, 10)
    go fibonacci(cap(c), c)
    // range 函数遍历每个从通道接收到的数据,因为 c 在发送完10个数据之后就关闭了通道。
    // 所以这里我们 range 函数在接收到 10 个数据之后就结束了。
    // 如果上面的 c 通道不关闭,那么 range 函数就不会结束,从而在接收第 11 个数据的时候就阻塞了。
 
    for i := range c {
    
    
            fmt.Println(i)
    }
}

El resultado de la ejecución es:

0
1
1
2
3
5
8
13
21
34

Búfer de canal

Por defecto, el canal no tiene búfer. El remitente envía datos y, al mismo tiempo, debe haber datos de recepción correspondientes para el receptor.

El canal puede configurar el búfer. El tamaño del búfer se especifica mediante el segundo parámetro de make:

ch := make(chan int, 100)

El canal con búfer permite que la transmisión de datos del remitente y la adquisición de datos del receptor estén en un estado asincrónico, lo que significa que los datos enviados por el remitente pueden colocarse en el búfer y pueden esperar a que el receptor obtenga los datos, en lugar de requerir inmediatamente que el receptor los obtenga. .

Sin embargo, debido a que el tamaño del búfer es limitado, debe haber un extremo receptor para recibir los datos; de lo contrario, una vez que el búfer esté lleno, el extremo de envío de datos ya no podrá enviar datos.

Nota:
Si el canal no está almacenado en búfer, el remitente se bloqueará hasta que el receptor reciba el valor del canal. Si el canal está almacenado en búfer, el remitente bloqueará hasta que el valor enviado se copie en el búfer;
si el búfer está lleno, significa que debe esperar hasta que un receptor obtenga un valor. El receptor se bloqueará hasta que haya un valor para recibir.

Ejemplo:

package main



import "fmt"



func main() {
    
    

        //定义了一个可以存储整数类型的带缓冲通道
        // 缓冲区大小为2

        ch := make(chan int, 2)


        // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据
        // 而不用立刻需要去同步读取数据

        ch <- 1

        ch <- 2


        // 获取这两个数据

        fmt.Println(<-ch)

        fmt.Println(<-ch)

}

El resultado de la ejecución es:

1
2

Supongo que te gusta

Origin blog.csdn.net/zp17834994071/article/details/108760099
Recomendado
Clasificación