Thank You for Subscribing

Introduction

When I started to work with Go’s channels for the first time, I made the mistake of thinking about channels as a data structure. Eu via canais como uma fila que fornecia acesso automático sincronizado entre goroutines. Este entendimento estrutural me fez escrever um monte de código paralelo ruim e complicado.Aprendi com o tempo que é melhor esquecer como os canais são estruturados e focar em como eles se comportam. Então agora, quando se trata de canais, eu penso em uma coisa: sinalização. Um canal permite que um goroutine sinalize outro goroutine sobre um evento em particular. A sinalização está no centro de tudo o que você deve estar fazendo com os canais. Pensar em canais como um mecanismo de sinalização permitirá que você escreva um código melhor com um comportamento bem definido e mais preciso.

Para entender como sinalização de obras, devemos entender os seus três atributos de:

  • Garantia De Entrega
  • Estado
  • Com ou Sem Dados

Estes três atributos trabalham juntos para criar uma filosofia de design em torno de sinalização. Depois de discutir estes atributos, vou fornecer uma série de exemplos de código que demonstram sinalização com esses atributos aplicados.A garantia de entrega baseia-se numa questão: “Será que preciso de uma garantia de que o sinal enviado por um determinado goroutino foi recebido?”

Em outras palavras, dado este exemplo na listagem 1:

Listagem 1

01 go func() {02 p := <-ch // Receive03 }()0405 ch <- "paper" // Send

o envio de goroutine precisa de uma garantia de que o paper ser enviados através do canal na linha 05, foi recebido pelo goroutine na linha 02, antes de se passar?

com base na resposta a esta pergunta, você saberá qual dos dois tipos de canais a usar: livre ou Buffer. Cada canal fornece um comportamento diferente em torno de garantias de entrega.

Figura 1 : Garantia De Entrega

Garantia De Entrega

Garantias são importantes, e, se você não pensa assim, eu tenho uma tonelada de coisas que eu quero vendê-lo. Claro que estou a tentar fazer uma piada, mas não ficas nervoso quando não tens garantias na vida? Ter uma forte compreensão de se você precisa ou não de uma garantia é crucial ao escrever software concorrente. Enquanto continuamos, vais aprender a decidir.

Estado

o comportamento de um canal é directamente influenciado pelo seu estado actual. O estado de um canal pode ser nulo, aberto ou fechado.

Listagem 2 abaixo mostra como declarar ou colocar um canal em cada um destes três estados.

Listagem 2

// ** nil channel// A channel is in a nil state when it is declared to its zero valuevar ch chan string// A channel can be placed in a nil state by explicitly setting it to nil.ch = nil// ** open channel// A channel is in a open state when it's made using the built-in function make.ch := make(chan string) // ** closed channel// A channel is in a closed state when it's closed using the built-in function close.close(ch)

o estado determina como as operações de envio e recepção se comportam.

sinais são enviados e recebidos através de um canal. Não diga ler / escrever porque os canais não executam I/O

Figura 2 : Estado

Estado

quando um canal está num estado nulo, qualquer envio ou recepção no canal irá bloquear. Quando um canal está em estado aberto, os sinais podem ser enviados e recebidos. Quando um canal é colocado em um estado fechado, os sinais não podem mais ser enviados, mas ainda é possível receber sinais.

estes estados irão fornecer os diferentes comportamentos de que necessita para as diferentes situações que encontra. Ao combinar Estado com garantia de entrega, você pode começar a analisar os custos/benefícios que você está incorrendo como resultado de suas escolhas de design. Em muitos casos, você também será capaz de detectar rapidamente bugs apenas lendo o código, porque você entende como o canal vai se comportar.

com e sem dados

o último atributo de sinalização que precisa ser tido em conta é se você precisa de sinalizar com ou sem dados.

você sinaliza com dados executando um envio em um canal.

Listagem 3

01 ch <- "paper"

quando você faz sinal com dados, geralmente é porque:

  • pede-se a um goroutino que inicie uma nova tarefa.
  • um goroutino relata um resultado.

sinaliza sem dados ao fechar um canal.

Listagem 4

01 close(ch)

quando você faz sinal sem dados, geralmente é porque:

  • diz-se a um goroutino que pare o que está a fazer.
  • um goroutino relata que eles são feitos sem resultado.
  • a goroutine reports that it has completed processing and shut down.

existem exceções a estas regras, mas estes são os principais casos de uso e os que vamos focar neste post. Considero que as excepções a estas regras são um cheiro de código inicial.

um benefício da sinalização sem dados é que uma única goroutina pode sinalizar muitas goroutinas ao mesmo tempo. Sinalizar com dados é sempre uma troca de 1 para 1 entre goroutines.

sinalização com dados

quando você vai sinalizar com dados, existem três opções de configuração do canal que você pode escolher, dependendo do tipo de garantia que você precisa.

Figura 3 : Sinalização com dados

as três opções do canal são livres, tamponadas > 1 ou tamponadas =1.

  • garantia

    • um canal livre dá-Lhe a garantia de que um sinal a ser enviado foi recebido.
      • porque a recepção do sinal acontece antes do envio do sinal terminar.
  • Nenhuma Garantia

    • Tamponada canal do tamanho >1 dá Nenhuma Garantia de que um sinal está sendo enviado foi recebido.
      • porque o envio do sinal acontece antes da recepção do sinal terminar.
  • garantia diferida

    • um canal tamponado de tamanho = 1 dá-lhe uma garantia atrasada. Pode garantir que o sinal anterior que foi enviado foi recebido.
      • porque a recepção do primeiro sinal, acontece antes do envio do segundo sinal completar.

O tamanho do buffer nunca deve ser um número aleatório, Ele sempre deve ser calculado para alguns bem definidos restrição. Não há infinito na computação, tudo deve ter alguma restrição bem definida, seja o tempo ou o espaço.

sinalização sem dados

sinalização sem dados é reservado principalmente para cancelamento. Permite a um goroutino sinalizar a outro goroutino para cancelar o que estão fazendo e seguir em frente. O cancelamento pode ser implementado usando canais não-Barrados e Buffered, mas usando um canal buffer quando nenhum dado será enviado é um cheiro de código.

Figura 4 : Sinalização sem dados

a função incorporada close é usada para sinalizar sem dados. Como explicado acima na seção de estado, você ainda pode receber sinais em um canal que está fechado. Na verdade, qualquer recepção em um canal fechado não vai bloquear e a operação de recepção sempre retorna.

na maioria dos casos você quer usar a biblioteca padrão context pacote para implementar sinalização sem dados. O pacote context usa um canal livre por baixo para a sinalização e a função incorporada close para sinalizar sem dados.

se optar por usar o seu próprio canal para o cancelamento, em vez do pacote de contexto, o seu canal deve ser do tipo chan struct{}. É a forma idiomática de indicar um canal usado apenas para sinalização.

cenários

com estes atributos no lugar, a melhor maneira de compreender melhor como eles funcionam na prática é executar através de uma série de cenários de código. Eu gosto de pensar em goroutines como pessoas quando estou lendo e escrevendo código baseado em canais. Esta visualização realmente ajuda, e eu vou usá-la como uma ajuda abaixo.

Signal With Data – Guarantee – Unbuffered Channels

When you need to know that a signal being received, two scenarios come in play. Estes são esperar pela tarefa e esperar pelo resultado.

Cenário 1-espere pela tarefa

pense em ser um gerente e contratar um novo empregado. Neste cenário, você quer que seu novo funcionário para executar uma tarefa, mas eles precisam esperar até que você esteja pronto. Isto é porque precisas de lhes dar um pedaço de papel antes de começarem.

Listagem 5
https://play.golang.org/p/BnVEHRCcdh

01 func waitForTask() {02 ch := make(chan string)0304 go func() {05 p := <-ch0607 // Employee performs work here.0809 // Employee is done and free to go.10 }()1112 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)1314 ch <- "paper"15 }

Na linha 02 na listagem 5, um Unbuffered canal é criado com o atributo string dados serão enviados com o sinal. Em seguida, na linha 04, um funcionário é contratado e disse para esperar pelo seu sinal na linha 05 antes de fazer o seu trabalho. A linha 05 é o canal recebido, fazendo com que o funcionário bloqueie enquanto espera pelo pedaço de papel que você vai enviar. Uma vez que o papel é recebido pelo empregado, o empregado executa o trabalho e, em seguida, é feito e livre para ir.

como gerente, está a trabalhar em simultâneo com o seu novo empregado. Então, depois de contratar o empregado na linha 04, você se encontra (na linha 12) fazendo o que você precisa fazer para desbloquear e sinalizar o empregado. Nota, não se sabia quanto tempo levaria para preparar este pedaço de papel que você precisa enviar.

eventualmente você está pronto para sinalizar o empregado. Na linha 14, você executa um sinal com dados, sendo os dados aquele pedaço de papel. Uma vez que um canal livre está sendo usado, você recebe uma garantia de que o funcionário recebeu o papel assim que sua operação de envio terminar. A recepção acontece antes do envio.Tecnicamente, tudo o que sabe é que o empregado tem o papel quando o seu canal envia a operação completa. Após ambas as operações do canal, o scheduler pode optar por executar qualquer declaração que quiser. A próxima linha de código que é executada por si ou pelo empregado é não-determinística. Isto significa que o uso de declarações impressas pode enganá-lo sobre a ordem das coisas.

Cenário 2-esperar pelo resultado

neste próximo cenário as coisas são revertidas. Desta vez você quer que seu novo empregado para executar uma tarefa imediatamente quando eles são contratados, e você precisa esperar pelo resultado do seu trabalho. Você precisa esperar porque você precisa do papel deles antes que você possa continuar.

Listagem 6
https://play.golang.org/p/VFAWHxIQTP

01 func waitForResult() {02 ch := make(chan string)0304 go func() {05 time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)0607 ch <- "paper"0809 // Employee is done and free to go.10 }()1112 p := <-ch13 }

Na linha 02 na listagem 6, um Unbuffered canal é criado com o atributo string dados serão enviados com o sinal. Em seguida, na linha 04, um funcionário é contratado e é imediatamente colocado no trabalho. Depois de contratar o empregado na linha 04, encontra-se a seguir na linha 12 à espera do relatório do jornal.

uma vez que o trabalho é concluído pelo empregado na linha 05, eles enviam o resultado para você na linha 07 através da realização de um canal enviar com dados. Uma vez que este é um canal livre, a recepção acontece antes do envio e o funcionário é garantido que você recebeu o resultado. Uma vez que o empregado tem esta garantia, eles são feitos e livres para ir. Neste cenário, Você não tem idéia de quanto tempo vai levar o funcionário para terminar a tarefa.

Custo / benefício

um canal livre fornece uma garantia de que um sinal a ser enviado foi recebido. Isto é óptimo, mas nada é de graça. O custo desta garantia é uma latência desconhecida. No cenário de espera pela tarefa, o empregado não faz ideia de quanto tempo vai demorar para você enviar o jornal. No cenário de espera pelo resultado, você não tem idéia de quanto tempo vai levar o funcionário para enviar-lhe esse resultado.

em ambos os cenários, esta latência desconhecida é algo com que temos de viver porque a garantia é necessária. A lógica não funciona sem este comportamento garantido.

sinal com canais sem garantia>1

quando você não precisa saber que um sinal que está sendo enviado foi recebido, estes dois cenários entram em jogo: espalhem-se e abandonem.

um canal buffer tem um espaço bem definido que pode ser usado para armazenar os dados que estão sendo enviados. Então, como é que decides de quanto espaço precisas? Responda a estas perguntas.:

  • eu tenho uma quantidade bem definida de trabalho a ser concluído?
    • quanto trabalho há?
  • se o meu empregado não consegue acompanhar, posso descartar algum trabalho novo?
    • quanto trabalho excepcional me coloca na capacidade?
  • que nível de risco estou disposto a aceitar se o meu programa terminar inesperadamente?
    • tudo o que esperar no buffer será perdido.

se estas perguntas não fazem sentido para o comportamento que você está modelando, é um cheiro de código que usando um canal buffer qualquer maior do que 1 é provavelmente errado.

Scenario 1-Fan Out

a fan out pattern allows you to throw a well defined number of employees at a problem who work concurrently. Uma vez que você tem um empregado para cada tarefa, você sabe exatamente quantos relatórios você receberá. Você pode se certificar de que há a quantidade certa de espaço em sua caixa para receber todos esses relatórios. Isto tem o benefício de seus funcionários não precisando esperar por você para apresentar seu relatório. No entanto, eles precisam de cada vez colocar o relatório em sua caixa se eles chegam à caixa em ou perto do mesmo tempo.Imagine que é novamente o gerente, mas desta vez contrata uma equipa de empregados. Você tem uma tarefa individual que quer que cada funcionário execute. Como cada funcionário individual termina sua tarefa, eles precisam fornecer-lhe um relatório de papel que deve ser colocado em sua caixa em sua mesa.

listagem 7
https://play.golang.org/p/8HIt2sabs_

01 func fanOut() {02 emps := 2003 ch := make(chan string, emps)0405 for e := 0; e < emps; e++ {06 go func() {07 time.Sleep(time.Duration(rand.Intn(200)) * time.Millisecond)08 ch <- "paper"09 }()10 }1112 for emps > 0 {13 p := <-ch14 fmt.Println(p)15 emps--16 }17 }

na linha 03 na lista 7, um canal buffer é criado com o atributo de que string dados serão enviados com o sinal. Desta vez, o canal é criado com 20 tampões graças à variável emps declarada na linha 02.

entre as linhas 05 a 10, 20 empregados são contratados e eles imediatamente começam a trabalhar. Não fazes ideia de quanto tempo cada empregado vai demorar na linha 07. Em seguida, na linha 08, os funcionários enviam o relatório de papel, mas desta vez o envio não bloqueia à espera de uma recepção. Uma vez que há espaço na caixa para cada funcionário, o envio no canal está apenas competindo com outros funcionários que podem querer enviar o seu relatório em ou perto do mesmo tempo.

o código entre as linhas 12 a 16 é todo seu. É aqui que você espera que todos os 20 funcionários terminem seu trabalho e enviem seu relatório. Na linha 12, Você está em um loop e na linha 13 Você está bloqueado em um canal receber esperando por seus relatórios. Uma vez que um relatório é recebido, o relatório é impresso na linha 14 e a variável de contador local é decretada para indicar que um empregado é feito.

Cenário 2-queda

um padrão de queda permite que você jogue o trabalho fora quando o(s) Seu (s) empregado (s) estão na capacidade. Isso tem o benefício de continuar a aceitar o trabalho de seus clientes e nunca aplicar pressão ou latência na aceitação desse trabalho. A chave aqui é saber quando você está realmente na capacidade para que você não se comprometa com a quantidade de trabalho que você vai tentar fazer. Normalmente, testes de integração ou métricas é o que você precisa para ajudá-lo a identificar este número.Imagine que é o gerente de novo e contrata um único empregado para fazer o trabalho. Tem uma tarefa individual que quer que o empregado execute. Como o empregado termina sua tarefa você não se importa de saber que eles estão feitos. Tudo o que é importante é se você pode ou não colocar novo trabalho na caixa. Se você não pode realizar o envio, então você sabe que sua caixa está cheia e o empregado está na capacidade. Neste momento, o novo trabalho precisa ser descartado para que as coisas possam continuar se movendo.

listagem 8
https://play.golang.org/p/PhFUN5itiv

01 func selectDrop() {02 const cap = 503 ch := make(chan string, cap)0405 go func() {06 for p := range ch {07 fmt.Println("employee : received :", p)08 }09 }()1011 const work = 2012 for w := 0; w < work; w++ {13 select {14 case ch <- "paper":15 fmt.Println("manager : send ack")16 default:17 fmt.Println("manager : drop")18 }19 }2021 close(ch)22 }

na linha 03 na lista 8, um canal buffer é criado com o atributo que string dados serão enviados com o sinal. Desta vez o canal é criado com 5 buffers graças à constante cap declarada na linha 02.

entre as linhas 05 a 09 um único empregado é contratado para lidar com o trabalho. O for range é utilizado para a recepção do canal. Cada vez que um pedaço de papel é recebido é processado na linha 07.Entre as linhas 11 a 19 tenta enviar 20 pedaços de papel ao seu empregado. Desta vez é usada uma declaração select para realizar o envio dentro da primeira case na linha 14. Uma vez que a cláusula default está a ser usada dentro do select na linha 16, se o envio vai bloquear porque não há mais espaço no buffer, o envio é abandonado executando a linha 17.

finalmente na linha 21, a função incorporada close é chamada contra o canal. Isto irá sinalizar sem dados para o empregado que eles são feitos e livre para ir uma vez que eles completaram o seu trabalho atribuído..

Custo / benefício

um canal tamponado superior a 1 não garante que um sinal a ser enviado seja recebido. Há um benefício de se afastar desta garantia, que é a menor ou nenhuma latência na comunicação entre duas goroutinas. No cenário de Fan Out, há um espaço de buffer para cada funcionário que estará enviando um relatório. No cenário de queda, o buffer é medido para a capacidade e se a capacidade é alcançada trabalho é deixado cair para que as coisas possam continuar se movendo.

em ambas as opções, esta falta de garantia é algo com que temos de viver porque a redução da latência é mais importante. A exigência de latência zero a mínima não coloca um problema à lógica geral do sistema.

Signal With Data-Delayed Guarantee-buffer Channel 1

When it’s necessary to know if the previous signal that was sent has been received before sending a new signal, the Wait For Tasks scenario come into play.

Cenário 1-espere por tarefas

neste cenário você tem um novo empregado, mas eles vão fazer mais do que apenas uma tarefa. Você vai alimentá-los de muitas tarefas, uma após a outra. No entanto, eles devem terminar cada tarefa individual antes de começar uma nova. Uma vez que só podem trabalhar numa tarefa de cada vez, pode haver problemas de latência entre a transferência do trabalho. Se a latência pudesse ser reduzida sem perder a garantia de que o empregado está trabalhando na próxima tarefa, poderia ajudar.

é aqui que um canal tamponado de 1 tem benefício. Se tudo está correndo no ritmo esperado entre você e o empregado, nenhum de vocês vai precisar esperar pelo outro. Cada vez que você envia um pedaço de papel, o buffer está vazio. Cada vez que o seu empregado procura mais trabalho, o buffer está cheio. É uma simetria perfeita do fluxo de trabalho.

a melhor parte é esta. Se em algum momento você tentar enviar um pedaço de papel e você não pode porque o buffer está cheio, você sabe que seu empregado está tendo um problema e você parar. É aqui que entra a garantia atrasada. Quando o buffer está vazio e você executa o envio, você tem a garantia de que o seu empregado tomou o último pedaço de trabalho que Você enviou. Se efectuar o envio e você não pode, você tem a garantia de que eles não têm.

Listagem 9
https://play.golang.org/p/4pcuKCcAK3

01 func waitForTasks() {02 ch := make(chan string, 1)0304 go func() {05 for p := range ch {06 fmt.Println("employee : working :", p)07 }08 }()0910 const work = 1011 for w := 0; w < work; w++ {12 ch <- "paper"13 }1415 close(ch)16 }

Na linha 02 na listagem 9, um Buffer do canal de tamanho 1 é criado com o atributo string dados serão enviados com o sinal. Entre as linhas 04 a 08 um único funcionário é contratado para lidar com o trabalho. O for range é utilizado para a recepção do canal. Cada vez que um pedaço de papel é recebido é processado na linha 06.

entre as linhas 10 a 13 você começa a enviar suas tarefas para o empregado. Se o seu empregado pode correr o mais rápido que puder, a latência entre vocês os dois é reduzida. Mas com cada envio que você executar com sucesso, você tem a garantia de que o último pedaço de trabalho que você submeteu está sendo trabalhado.

finalmente na linha 15, a função incorporada close é chamada contra o canal. Isto irá sinalizar sem dados para o empregado que eles são feitos e livre para ir. No entanto, o último trabalho que apresentou será recebido (corado) antes de terminar o for range.

sinal sem contexto de dados

neste último cenário você verá como você pode cancelar uma goroutina em execução usando um valor Context do pacote context. Tudo isso funciona alavancando um canal livre que é fechado para realizar um sinal sem dados.

você é o Gerente uma última vez e você contratar um único empregado para fazer o trabalho feito. Desta vez você não está disposto a esperar por alguma quantidade desconhecida de tempo para o funcionário terminar. Você está em um prazo discreto e se o empregado não termina a tempo, você não está disposto a esperar.

listagem 10
https://play.golang.org/p/6GQbN5Z7vC

01 func withTimeout() {02 duration := 50 * time.Millisecond0304 ctx, cancel := context.WithTimeout(context.Background(), duration)05 defer cancel()0607 ch := make(chan string, 1)0809 go func() {10 time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)11 ch <- "paper"12 }()1314 select {15 case p := <-ch:16 fmt.Println("work complete", p)1718 case <-ctx.Done():19 fmt.Println("moving on")20 }21 }

na linha 02 na lista 10, é declarado um valor de duração que representa quanto tempo o empregado terá de terminar a tarefa. Este valor é usado na linha 04 para criar um valor context.Context com um tempo-limite de 50 milisegundos. A função WithTimeout do pacote context devolve um valor Context e uma função de cancelamento.

o pacote context cria uma goroutina que irá fechar o canal livre associado com o valor Context uma vez que a duração é cumprida. Você é responsável por chamar a função cancel independentemente de como as coisas acabam. Isto vai limpar as coisas que foram criadas para o Context. É ok para a função cancel ser chamada mais de uma vez.

na linha 05, a função cancel é adiada para ser executada uma vez que esta função termine. Na linha 07 é criado um canal buffer de 1, que será usado pelo empregado para enviar-lhe o resultado de seu trabalho. Em seguida, nas linhas 09 a 12, o funcionário é contratado e imediatamente colocado no trabalho. Não fazes ideia de quanto tempo vai demorar o empregado a acabar.

entre as linhas 14 a 20 você usa a declaração select para receber em dois canais. A recepção na linha 15, você espera que o funcionário lhe envie o resultado. A recepção na linha 18, você espera para ver se o pacote context vai sinalizar que os 50 milisegundos está para cima. O sinal que receber primeiro será o processado.

um aspecto importante deste algoritmo é o uso do canal buffer de 1. Se o empregado não terminar a tempo, você está avançando sem dar ao empregado qualquer aviso prévio. Do ponto de vista dos empregados, eles sempre lhe enviarão o relatório na linha 11 e eles são cegos se você está lá ou não para recebê-lo. Se você usar um canal livre, o empregado irá bloquear para sempre tentando enviar-lhe o relatório se você seguir em frente. Isso criaria uma fuga de goroutine. Assim, um canal buffer de 1 está sendo usado para evitar que isso aconteça.

conclusão

os atributos de sinalização em torno de garantias, estado do canal e envio são importantes para conhecer e compreender quando se utilizam canais (ou concorrência). Eles irão ajudá-lo a implementar o melhor comportamento que você precisa para os programas e algoritmos concorrentes que você está escrevendo. Eles vão ajudar-te a encontrar insectos e a descobrir um código potencialmente mau.

neste post eu compartilhei alguns programas de exemplo que mostram como os atributos de sinalização funcionam em diferentes cenários. Há exceções a todas as regras, mas esses padrões são uma boa base para começar.

reveja estes esboços como um resumo de quando e como efetivamente pensar sobre e usar canais:

mecânica da linguagem

  • usa canais para orquestrar e coordenar goroutinas.
    • Foque nos atributos de sinalização e não na partilha de dados.Sinalização com ou sem dados.
    • questionam o seu uso para sincronizar o acesso ao estado compartilhado.
      • existem casos em que os canais podem ser mais simples para esta questão, mas inicialmente.
  • canais livres:
    • receber acontece antes do envio.
    • benefício: 100% garantia o sinal foi recebido.
    • custo: Latência desconhecida ligada quando o sinal será recebido.
  • canais tamponados:
    • enviar acontece antes da recepção.Benefício :reduzir a latência bloqueante entre a sinalização.
    • custo: nenhuma garantia quando o sinal foi recebido.
      • quanto maior o buffer, menor a garantia.
      • Buffer of 1 pode dar – lhe um atraso no envio da garantia.
  • canais de Fecho:
    • Close acontece antes da recepção (como Buffer).
    • sinalização sem dados.
    • perfeito para sinalizar cancelamentos e prazos.
  • canais nulos:
    • enviar e receber bloco.
    • desligar a sinalização
    • perfeito para a limitação da velocidade ou para as paragens a curto prazo.

filosofia de Design

  • se algum envio dado num canal pode fazer com que a goroutina de envio bloqueie:
    • não é permitida a utilização de um canal buffer maior que 1.
      • os tampões maiores que 1 devem ter razão/medições.
    • deve saber o que acontece quando os goroutinos mandam bloquear.
  • se algum envio dado num canal não fizer com que o goroutine de envio bloqueie:
    • tem o número exacto de ‘buffers’ para cada envio.
      • padrão de espalhamento
    • você tem o buffer medido para a capacidade máxima.
      • padrão de queda
  • menos é mais com buffers.
    • não pense no desempenho ao pensar em tampões.Os tampões de choque podem ajudar a reduzir a latência de bloqueio entre a sinalização.
      • a redução da latência de bloqueio para zero não significa necessariamente uma melhor produtividade.
      • se um tampão de um Lhe estiver a dar um rendimento suficiente, então guarde-o.Tampões de interrogação
      • maiores do que um e medem o tamanho.
      • Encontre o menor buffer possível que forneça um rendimento suficiente.

Deixe uma resposta

O seu endereço de email não será publicado.