Compartilhamento de tecnologia

Linguagem Go --- canal de programação simultâneo (canal duplo, canal único) e exemplos de aplicação (produtor consumidor, modelo de impressora)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Canal

Goroutines são executadas no mesmo espaço de endereço, portanto o acesso à memória compartilhada deve ser sincronizado.Goroutines compartilham memória por meio da comunicação, em vez de compartilhar memória.

O canal do tipo de referência é uma implementação específica do modo CSP e é usado para comunicação com múltiplas goroutines. Ele implementa a sincronização internamente para garantir a segurança da simultaneidade.

tipo de canal

Assim como o mapa, o canal também é uma referência à estrutura de dados subjacente criada por make.
Quando copiamos um canal ou o usamos para passagem de parâmetros de função, apenas copiamos uma referência de canal, para que o chamador e o receptor façam referência ao mesmo objeto de canal. Como outros tipos de referência, o valor zero do canal é nulo.
Ao definir um canal, você também precisa definir o tipo de valores enviados ao canal. Canais podem ser criados usando a função integrada make():

make (chan Type)
make (chan Type,capacity)
  • 1
  • 2
  • Quando capacidade=0, o canal não tem buffer e bloqueia leitura e escrita. Quando capacidade>0, o canal possui cache e a escrita não será bloqueada até que os elementos de capacidade sejam preenchidos.
  • Chamnel recebe e envia dados através do operador <-. A sintaxe para enviar e receber dados é:
channel<-value   //发送value 到channel
<-channel     //接收并将其丢弃
x:=<-channel   //从 channel 中接收数据,并賦偵给x
x,ok:=<-channel   //功能同上,同时检查通道是否已关闭或者是否为空
  • 1
  • 2
  • 3
  • 4

Por padrão, o canal bloqueia ao receber e enviar dados, a menos que a outra extremidade esteja pronta, simplificando a sincronização da goroutine sem a necessidade de bloqueios explícitos.

concluir

Insira a descrição da imagem aqui

  • Os tubos são colocados um na frente da impressão e outro atrás da impressão.
  • Se Person2 for executado primeiro e não houver dados no pipeline, ele será bloqueado.
  • Person1 executa e após imprimir os dados, insira os dados no pipe. Person2 irá detectá-los e começar a imprimir.

Implementar sincronização e interação de dados por meio de canais

Insira a descrição da imagem aqui
Insira a descrição da imagem aqui

canal sem buffer

Um canal sem buffer é um canal que não tem capacidade de salvar nenhum valor antes de receber.
Este tipo de canal requer que a goroutine de envio e a goroutine de recebimento estejam prontas ao mesmo tempo para concluir as operações de envio e recebimento. Se duas goroutines não estiverem prontas ao mesmo tempo, o canal fará com que a goroutine que executa primeiro a operação de envio ou recebimento seja bloqueada e espere.
Esta interação de envio e recebimento no canal é inerentemente síncrona. Nenhuma operação pode existir independentemente da outra.
A figura a seguir mostra como duas goroutines podem compartilhar um valor usando canais sem buffer:
Insira a descrição da imagem aqui

  • Ou seja, o próprio canal não pode armazenar coisas. Se você colocar algo em um, poderá retirá-lo imediatamente.
  • Na etapa 1, ambas as goroutines chegam ao canal, mas nenhuma começa a enviar ou receber.
  • Na etapa 2, a goroutine da esquerda coloca a mão no canal, o que simula o ato de enviar dados para o canal. Neste momento, a goroutine ficará bloqueada no canal até que a troca seja concluída.
  • Na etapa 3, a goroutine da direita coloca a mão no canal, o que simula o recebimento de dados do canal. Esta goroutine também ficará bloqueada no canal até que a troca seja concluída.
  • Nas etapas 4 e 5 ocorre uma troca e, por fim, na etapa 6, ambas as goroutines tiram as mãos do canal, o que simula a liberação da goroutine bloqueada. Ambas as goroutines agora podem fazer outras coisas.

Crie um canal sem buffer

make (chan Type)//等价于make (chan Type,0)
  • 1

Se nenhum tamanho de buffer for especificado, o canal será síncrono e, portanto, será bloqueado até que o remetente esteja pronto para enviar e o receptor esteja pronto para receber.

Insira a descrição da imagem aqui

  • ch&lt;-i grava dados no canal. A corrotina principal não será executada até que seja detectada. Nesse momento, a sub-rotina também será bloqueada e aguardará.
  • No entanto, println imprime os dados após a leitura do pipe, portanto a velocidade de impressão é determinada pelo sistema.

canal em buffer

Um canal com buffer é um canal que pode armazenar um ou mais valores antes de serem recebidos.
Este tipo de canal não exige que a goroutine conclua o envio e o recebimento ao mesmo tempo. Os canais também terão condições diferentes que bloqueiam ações de envio e recebimento. A ação de recebimento só será bloqueada se não houver valor no canal a ser recebido. A ação de envio só será bloqueada se o canal não tiver buffer disponível para acomodar o valor que está sendo enviado.
Isso leva a uma grande diferença entre canais com buffer e sem buffer: canais sem buffer garantem que o envio e o recebimento de goroutines troquem dados ao mesmo tempo: os canais com buffer não têm essa garantia.
Insira a descrição da imagem aqui

  • Na etapa 1, a goroutine à direita está recebendo um valor do canal.
  • No passo 2, a goroutine da direita completa a ação de receber o valor de forma independente, enquanto a goroutine da esquerda envia um novo valor para o canal.
  • Na etapa 3, a goroutine da esquerda ainda está enviando um novo valor para o canal, enquanto a goroutine da direita está recebendo outro valor do canal. As duas operações nesta etapa não estão sincronizadas nem bloqueando uma à outra.
  • Por fim, na etapa 4, todo o envio e recebimento são concluídos, e ainda restam alguns valores no canal e algum espaço para armazenar mais valores.

criar

make (chan Type,capacity)
  • 1

Se for fornecida uma capacidade de buffer, o canal é assíncrono. Enquanto o buffer tiver espaço não utilizado para envio de dados ou contiver dados que possam ser recebidos, a comunicação prosseguirá sem bloqueio.

concluir

Insira a descrição da imagem aqui

  • Se três forem armazenados, ele bloqueará e aguardará a leitura. Quando a corrotina principal tiver lido um dado e houver espaço livre, a sub-rotina continuará a ser executada e a ordem de impressão subsequente não será certa.
    Insira a descrição da imagem aqui

fechar canal

O canal pode usar ok para detectar se o canal ainda está aberto. Se o canal estiver fechado, os dados não serão lidos.

Insira a descrição da imagem aqui

  • num ,ok:=<-chPode detectar se um canal está fechado.

Perceber

  • Os canais não precisam ser fechados com tanta frequência quanto os arquivos. Somente feche o canal quando você realmente não tiver nenhum dado para enviar, ou se quiser encerrar explicitamente o loop de intervalo ou algo semelhante;
  • Após fechar o canal, nenhum dado mais poderá ser enviado ao canal (é acionado um erro de pânico, fazendo com que a recepção retorne um valor zero imediatamente):
  • Após fechar o canal, você pode continuar recebendo dados do canal:
  • Para canais nulos, tanto o envio quanto o recebimento serão bloqueados.
    Insira a descrição da imagem aqui
  • Você pode usar o alcance para percorrer o canal e sair automaticamente do loop
    Insira a descrição da imagem aqui

Canal unidirecional

Por padrão, o canal é de pergunta dupla, ou seja, você pode enviar e receber dados dele.
Porém, muitas vezes vemos um canal passado como parâmetro e espera-se que o valor seja usado em uma direção, seja apenas para enviar dados ou apenas para receber dados. Neste momento, podemos especificar a direção do canal. A declaração de uma variável chamel unidirecional é muito simples, como segue:

var ch1 chan int //ch1双向
var ch2 chan<-float64 //ch2单向,只能用于写float64数据
var ch3 <-chan int  //ch3单向,只能用于读int数据
  • 1
  • 2
  • 3

chan&lt;- significa que os dados entram no canal e os dados são gravados no canal, que é enviado para o chamador.
&lt;-chan indica que os dados saem do canal. Para o chamador, os dados do canal são obtidos, que é obviamente a entrada.
Um canal pode ser convertido implicitamente em uma fila unidirecional, recebendo apenas ou apenas enviando, mas um canal unidirecional não pode ser convertido em um canal normal;

Insira a descrição da imagem aqui

Utilize um único canal para implementar o modelo produtor-consumidor

Insira a descrição da imagem aqui