Gracias por suscribirse

Introducción

Cuando empecé a trabajar con los canales de Go por primera vez, cometí el error de pensar en los canales como una estructura de datos. Vi los canales como una cola que proporcionaba acceso sincronizado automático entre goroutines. Esta comprensión estructural me hizo escribir un montón de código concurrente malo y complicado.

Con el tiempo aprendí que es mejor olvidarse de cómo están estructurados los canales y centrarse en cómo se comportan. Así que ahora, cuando se trata de canales, pienso en una cosa: la señalización. Un canal permite que un goroutine señale a otro goroutine sobre un evento en particular. La señalización es el núcleo de todo lo que debe hacer con los canales. Pensar en los canales como un mecanismo de señalización le permitirá escribir mejor código con un comportamiento bien definido y más preciso.

Para entender cómo funciona la señalización, debemos entender sus tres atributos:

  • Garantía de entrega
  • Estado
  • Con o Sin Datos

Estos tres atributos trabajan juntos para crear una filosofía de diseño en torno a la señalización. Después de discutir estos atributos, proporcionaré una serie de ejemplos de código que demuestran la señalización con estos atributos aplicados.

Garantía de entrega

La Garantía de entrega se basa en una pregunta: «¿Necesito una garantía de que se ha recibido la señal enviada por un goroutine en particular?»

En otras palabras, dado este ejemplo en el listado 1:

Listado 1

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

¿El goroutine que envía necesita una garantía de que el paper que se envía a través del canal en la línea 05 fue recibido por el goroutine en la línea 02 antes de continuar?

En función de la respuesta a esta pregunta, sabrá cuál de los dos tipos de canales usar: Sin búfer o con búfer. Cada canal proporciona un comportamiento diferente en torno a las garantías de entrega.

Figura 1 : la Garantía De Entrega

Garantía De Entrega

Garantías son importantes, y, si no lo creo, tengo un montón de cosas que quiero vender. Por supuesto, estoy tratando de hacer una broma, pero ¿no te pones nervioso cuando no tienes garantías en la vida? Tener una sólida comprensión de si necesita o no una garantía es crucial al escribir software simultáneo. A medida que continuemos, aprenderás a decidir.

Estado

El comportamiento de un canal está directamente influenciado por su estado actual. El estado de un canal puede ser nulo, abierto o cerrado.

El listado 2 a continuación muestra cómo declarar o colocar un canal en cada uno de estos tres estados.

Listado 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)

El estado determina cómo se comportan las operaciones de envío y recepción.Las señales

se envían y reciben a través de un canal. No diga lectura / escritura porque los canales no realizan E / S.

Figura 2 : Estado

Estado

Cuando un canal está en estado cero, cualquier intento de envío o recepción en el canal se bloqueará. Cuando un canal está en estado abierto, se pueden enviar y recibir señales. Cuando un canal se coloca en un estado cerrado, las señales ya no se pueden enviar, pero aún es posible recibir señales.

Estos estados proporcionarán los diferentes comportamientos que necesita para las diferentes situaciones que encuentre. Al combinar el Estado con la Garantía de entrega, puede comenzar a analizar los costos/beneficios en los que incurre como resultado de sus elecciones de diseño. En muchos casos, también podrá detectar errores rápidamente con solo leer el código, porque entiende cómo se comportará el canal.

Con y sin Datos

El último atributo de señalización que debe tenerse en cuenta es si necesita señalizar con o sin datos.

Señalas con datos al realizar un envío en un canal.

Listado 3

01 ch <- "paper"

Cuando haces señales con datos, por lo general es porque:

  • Se le pide a un goroutine que inicie una nueva tarea.
  • Un goroutine informa de un resultado.

Usted hace señal sin datos cerrando un canal.

Listado 4

01 close(ch)

Cuando haces una señal sin datos, por lo general es porque:

  • A un goroutine se le dice que deje de hacer lo que está haciendo.
  • Un goroutine informa de que se han realizado sin resultados.
  • Un goroutine informa que ha completado el procesamiento y se ha apagado.

Hay excepciones a estas reglas, pero estos son los principales casos de uso y los que nos centraremos en esta publicación. Considero que las excepciones a estas reglas son un olor a código inicial.

Un beneficio de la señalización sin datos es que una sola goroutina puede señalar muchas goroutinas a la vez. La señalización con datos es siempre un intercambio de 1 a 1 entre goroutinas.

Señalización con datos

Cuando va a señalizar con datos, hay tres opciones de configuración de canal que puede elegir según el tipo de garantía que necesite.

Figura 3 : Señalización Con Datos

Las tres opciones de canal son Sin búfer, con Búfer > 1 o con búfer =1.

  • Garantía

    • Un canal sin búfer le garantiza que se ha recibido una señal que se está enviando.
      • Porque la Recepción de la señal Ocurre Antes de que se complete el Envío de la señal.
  • Sin garantía

    • Un canal en búfer de tamaño > 1 no le garantiza que se haya recibido una señal enviada.
      • Porque el Envío de la señal Ocurre Antes de que se complete la Recepción de la señal.
  • Garantía retrasada

    • Un canal con búfer de tamaño = 1 le da una Garantía retrasada. Puede garantizar que se ha recibido la señal anterior que se envió.
      • Porque la Recepción de la Primera Señal, Ocurre Antes de que se complete el Envío de la Segunda Señal.

El tamaño del búfer nunca debe ser un número aleatorio, siempre debe calcularse para alguna restricción bien definida. No hay infinito en la computación, todo debe tener alguna restricción bien definida, ya sea tiempo o espacio.

Señalización sin datos

La señalización sin datos está reservada principalmente para la cancelación. Permite que un goroutine señale a otro goroutine para cancelar lo que están haciendo y seguir adelante. La cancelación se puede implementar utilizando canales sin búfer y en búfer, pero usar un canal en búfer cuando no se enviarán datos es un olor a código.

Figura 4 : Señalización Sin Datos

La función incorporada close se utiliza para señalar sin datos. Como se explicó anteriormente en la sección Estado, aún puede recibir señales en un canal cerrado. De hecho, cualquier recepción en un canal cerrado no se bloqueará y la operación de recepción siempre regresa.

En la mayoría de los casos, desea usar el paquete biblioteca estándar context para implementar señalización sin datos. El paquete context utiliza un canal sin búfer debajo para la señalización y la función incorporada close para señalar sin datos.

Si elige usar su propio canal para la cancelación, en lugar del paquete de contexto, su canal debe ser de tipo chan struct{}. Es la forma idiomática de espacio cero para indicar un canal utilizado solo para la señalización.

Escenarios

Con estos atributos en su lugar, la mejor manera de comprender mejor cómo funcionan en la práctica es ejecutar una serie de escenarios de código. Me gusta pensar en goroutines como personas cuando estoy leyendo y escribiendo código basado en canales. Esta visualización realmente ayuda, y la usaré como ayuda a continuación.

Señal con Canales sin búfer con Garantía de datos

Cuando necesita saber que se ha recibido una señal enviada, entran en juego dos escenarios. Estos son Esperar a la Tarea y Esperar al Resultado.

Escenario 1-Espera la tarea

Piensa en ser gerente y contratar a un nuevo empleado. En este escenario, desea que su nuevo empleado realice una tarea, pero debe esperar hasta que usted esté listo. Esto se debe a que debe entregarles un pedazo de papel antes de que comiencen.

Listado 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 }

En la línea 02 del listado 5, se crea un canal sin búfer con el atributo que string se enviarán datos con la señal. Luego, en la línea 04, se contrata a un empleado y se le dice que espere su señal en la línea 05 antes de hacer su trabajo. La línea 05 es el canal que recibe, lo que hace que el empleado bloquee mientras espera el trozo de papel que enviará. Una vez que el empleado recibe el papel, el empleado realiza el trabajo y luego está listo y puede irse.

Usted, como gerente, está trabajando simultáneamente con su nuevo empleado. Entonces, después de contratar al empleado en la línea 04, se encuentra (en la línea 12) haciendo lo que necesita para desbloquear y señalar al empleado. Tenga en cuenta que no se sabía cuánto tiempo tomaría preparar este pedazo de papel que necesita enviar.

Eventualmente estará listo para hacer una señal al empleado. En la línea 14, se realiza una señal con datos, los datos son ese pedazo de papel. Dado que se está utilizando un canal sin búfer, obtienes una garantía de que el empleado ha recibido el papel una vez que se complete tu operación de envío. La recepción ocurre antes del envío.

Técnicamente, todo lo que sabes es que el empleado tiene el papel cuando se completa la operación de envío de tu canal. Después de ambas operaciones de canal, el programador puede elegir ejecutar cualquier instrucción que desee. La siguiente línea de código que ejecuta usted o el empleado no es determinada. Esto significa que usar declaraciones impresas puede engañarte sobre el orden de las cosas.

Escenario 2: Espere el resultado

En el siguiente escenario, las cosas se invierten. Esta vez, desea que su nuevo empleado realice una tarea inmediatamente cuando sea contratado, y debe esperar el resultado de su trabajo. Tienes que esperar porque necesitas el papel de ellos antes de poder continuar.

Listado 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 }

En la línea 02 del listado 6, se crea un canal sin búfer con el atributo que string se enviarán datos con la señal. Luego, en la línea 04, un empleado es contratado e inmediatamente puesto a trabajar. Después de contratar al empleado en la línea 04, se encontrará a continuación en la línea 12 esperando el informe en papel.

Una vez que el trabajo es completado por el empleado en la línea 05, le envían el resultado en la línea 07 realizando un canal de envío con datos. Dado que este es un canal sin búfer, la recepción ocurre antes del envío y el empleado tiene la garantía de que ha recibido el resultado. Una vez que el empleado tiene esta garantía, está listo y es libre de irse. En este escenario, no tiene idea de cuánto tiempo le llevará al empleado terminar la tarea.

Costo / Beneficio

Un canal sin búfer proporciona una garantía de que se recibió una señal enviada. Esto es genial, pero nada es gratis. El costo de esta garantía es latencia desconocida. En el escenario de Espera de tareas, el empleado no tiene idea de cuánto tiempo le llevará enviar ese documento. En el escenario de Espera de resultados, no tienes idea de cuánto tiempo le llevará al empleado enviarte ese resultado.

En ambos escenarios, esta latencia desconocida es algo con lo que tenemos que vivir porque se requiere la garantía. La lógica no funciona sin este comportamiento garantizado.

Señal Con Datos – Sin Garantía-Canales en Búfer >1

Cuando no necesita saber que se ha recibido una señal que se está enviando, entran en juego estos dos escenarios: Fan Out y Drop.

Un canal en búfer tiene un espacio bien definido que se puede usar para almacenar los datos que se envían. Entonces, ¿cómo decides cuánto espacio necesitas? Responda a estas preguntas:

  • ¿Tengo una cantidad de trabajo bien definida que completar?
    • ¿cuánto trabajo hay?
  • Si mi empleado no puede seguir el ritmo, ¿puedo descartar cualquier trabajo nuevo?
    • ¿Cuánto trabajo excepcional me pone al máximo?
  • ¿Qué nivel de riesgo estoy dispuesto a aceptar si mi programa termina inesperadamente?
    • Cualquier cosa que esté esperando en el búfer se perderá.

Si estas preguntas no tienen sentido para el comportamiento que está modelando, es un olor a código que usar un canal en búfer mayor que 1 probablemente esté mal.

Escenario 1-Salida en abanico

Un patrón de salida en abanico le permite lanzar un número bien definido de empleados a un problema que trabajan simultáneamente. Dado que tiene un empleado para cada tarea, sabe exactamente cuántos informes recibirá. Puede asegurarse de que haya la cantidad correcta de espacio en su caja para recibir todos esos informes. Esto tiene el beneficio de que sus empleados no necesitan esperar a que usted envíe su informe. Sin embargo, es necesario que cada uno tome un turno para colocar el informe en su caja si llegan a la caja a la misma hora o cerca de ella.

Imagina que eres el gerente de nuevo, pero esta vez contratas a un equipo de empleados. Tiene una tarea individual que desea que cada empleado realice. A medida que cada empleado termina su tarea, debe proporcionarle un informe en papel que debe colocar en su caja en su escritorio.

Listado 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 }

En la línea 03 del listado 7, se crea un canal en búfer con el atributo que string se enviarán datos con la señal. Esta vez el canal se crea con 20 buffers gracias a la variable emps declarada en la línea 02.

Entre las líneas 05 a 10, 20 empleados son contratados e inmediatamente se ponen a trabajar. No tienes idea de cuánto tiempo tomará cada empleado en la línea 07. Luego, en la línea 08, los empleados envían el informe en papel, pero esta vez el envío no bloquea la espera de una recepción. Dado que hay espacio en la caja para cada empleado, el envío en el canal solo compite con otros empleados que pueden querer enviar su informe al mismo tiempo o cerca de él.

El código entre las líneas 12 a 16 es todo tuyo. Aquí es donde esperas a que los 20 empleados terminen su trabajo y envíen su informe. En la línea 12, estás en un bucle y en la línea 13 estás bloqueado en un canal que recibe a la espera de tus informes. Una vez que se recibe un informe, el informe se imprime en la línea 14 y la variable del contador local se decrementa para indicar que un empleado ha terminado.

Escenario 2-Caída

Un patrón de caída le permite desechar el trabajo cuando sus empleados están a su capacidad. Esto tiene el beneficio de seguir aceptando el trabajo de sus clientes y nunca aplicar contrapresión o latencia en la aceptación de ese trabajo. La clave aquí es saber cuándo está realmente a su capacidad para que no se comprometa por debajo o por encima de la cantidad de trabajo que intentará hacer. Por lo general, las pruebas de integración o las métricas son lo que necesita para ayudarlo a identificar este número.

Imagina que eres el gerente de nuevo y contratas a un solo empleado para hacer el trabajo. Tiene una tarea individual que desea que el empleado realice. A medida que el empleado termina su tarea, no le importa saber que ha terminado. Todo lo que es importante es si puedes o no colocar trabajo nuevo en la caja. Si no puede realizar el envío, entonces sabrá que su caja está llena y que el empleado está a su capacidad. En este punto, el nuevo trabajo debe descartarse para que las cosas puedan seguir avanzando.

Listado 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 }

En la línea 03 del listado 8, se crea un canal en búfer con el atributo que string se enviarán datos con la señal. Esta vez el canal se crea con 5 buffers gracias a la constante cap declarada en la línea 02.

Entre las líneas 05 a 09 se contrata a un solo empleado para que se encargue del trabajo. Se utiliza un for range para la recepción del canal. Cada vez que se recibe un pedazo de papel, se procesa en la línea 07.

Entre las líneas 11 a 19, intenta enviar 20 piezas de papel a su empleado. Esta vez se usa una instrucción select para realizar el envío dentro de la primera case en la línea 14. Dado que la cláusula default se está utilizando dentro de select en la línea 16, si el envío se va a bloquear porque no hay más espacio en el búfer, el envío se abandona ejecutando la línea 17.

Finalmente en la línea 21, la función incorporada close se llama contra el canal. Esto señalará sin datos al empleado que están listos y libres de irse una vez que completen su trabajo asignado..

Costo / Beneficio

Un canal con búfer superior a 1 no garantiza que se reciba una señal que se envía. Hay un beneficio de alejarse de esta garantía, que es la latencia reducida o nula en la comunicación entre dos goroutinas. En el escenario de separación en abanico, hay un espacio de búfer para cada empleado que enviará un informe. En el escenario de caída, el búfer se mide para la capacidad y, si se alcanza la capacidad, el trabajo se cae para que las cosas puedan seguir en movimiento.

En ambas opciones, esta falta de garantía es algo con lo que tenemos que convivir porque la reducción de la latencia es más importante. El requisito de latencia de cero a mínima no plantea un problema para la lógica general del sistema.

Señal Con Garantía de retraso de datos – Canal de almacenamiento en búfer 1

Cuando es necesario saber si se ha recibido la señal anterior que se envió antes de enviar una nueva señal, entra en juego el escenario de Espera de tareas.

Escenario 1: Espere tareas

En este escenario, tiene un nuevo empleado, pero va a hacer más de una tarea. Vas a darles de comer muchas tareas, una tras otra. Sin embargo, deben terminar cada tarea individual antes de comenzar una nueva. Dado que solo pueden trabajar en una tarea a la vez, podría haber problemas de latencia entre la entrega del trabajo. Si la latencia se pudiera reducir sin perder la garantía de que el empleado está trabajando en la siguiente tarea, podría ayudar.

Aquí es donde un canal en búfer de 1 tiene beneficio. Si todo está funcionando al ritmo esperado entre usted y el empleado, ninguno de los dos tendrá que esperar al otro. Cada vez que envías un trozo de papel, el búfer está vacío. Cada vez que su empleado busca más trabajo, el búfer está lleno. Es una simetría perfecta de flujo de trabajo.

La mejor parte es esta. Si en algún momento intenta enviar un pedazo de papel y no puede porque el búfer está lleno, sabe que su empleado tiene un problema y se detiene. Aquí es donde entra en juego la garantía retrasada. Cuando el búfer está vacío y realiza el envío, tiene la garantía de que su empleado ha tomado la última pieza de trabajo que envió. Si realizas el envío y no puedes, tienes la garantía de que no lo han hecho.

Listado 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 }

En la línea 02 del listado 9, se crea un canal en búfer de tamaño 1 con el atributo que string se enviarán datos con la señal. Entre las líneas 04 y 08, se contrata a un solo empleado para que se encargue del trabajo. Se utiliza un for range para la recepción del canal. Cada vez que se recibe un pedazo de papel, se procesa en la línea 06.

Entre las líneas 10 a 13, comienza a enviar sus tareas al empleado. Si su empleado puede correr tan rápido como usted puede enviar, la latencia entre ustedes dos se reduce. Pero con cada envío que realice con éxito, tiene la garantía de que se está trabajando en la última pieza de trabajo que envió.

Finalmente en la línea 15, la función incorporada close se llama contra el canal. Esto señalará sin datos al empleado que están listos y libres de usar. Sin embargo, la última pieza de trabajo que envió se recibirá (se eliminará) antes de que termine for range.

Señal Sin Contexto de datos

En este último escenario verá cómo puede cancelar una goroutine en ejecución utilizando un valor Context del paquete context. Todo esto funciona aprovechando un canal sin búfer que está cerrado para realizar una señal sin datos.

Usted es el gerente por última vez y contrata a un solo empleado para realizar el trabajo. Esta vez no está dispuesto a esperar una cantidad de tiempo desconocida para que el empleado termine. Estás en un plazo discreto y si el empleado no termina a tiempo, no estás dispuesto a esperar.

Listado 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 }

En la línea 02 del listado 10, se declara un valor de duración que representa cuánto tiempo tendrá el empleado para terminar la tarea. Este valor se utiliza en la línea 04 para crear un valor context.Context con un tiempo de espera de 50 milisegundos. La función WithTimeout del paquete context devuelve un valor Context y una función de cancelación.

El paquete context crea una goroutine que cerrará el canal sin búfer asociado con el valor Context una vez que se cumpla la duración. Usted es responsable de llamar a la función cancel independientemente de cómo resulten las cosas. Esto limpiará las cosas que se han creado para Context. Está bien que la función cancel se llame más de una vez.

En la línea 05, la función cancel se diferirá para ejecutarse una vez que finalice esta función. En la línea 07 se crea un canal en búfer de 1, que va a ser utilizado por el empleado para enviarle el resultado de su trabajo. Luego, en las líneas 09 a 12, el empleado es contratado e inmediatamente puesto a trabajar. No tienes idea de cuánto tiempo le va a llevar al empleado terminar.

Entre las líneas 14 a 20 se utiliza la instrucción select para recibir en dos canales. Al recibir en la línea 15, espera a que el empleado le envíe su resultado. La recepción en la línea 18, espera a ver si el paquete context va a indicar que los 50 milisegundos han terminado. La señal que reciba primero será la que se procese.

Un aspecto importante de este algoritmo es el uso del canal de búfer de 1. Si el empleado no termina a tiempo, usted está avanzando sin avisarle al empleado. Desde la perspectiva de los empleados, siempre le enviarán el informe en la línea 11 y están ciegos si usted está allí o no para recibirlo. Si usas un canal sin búfer, el empleado bloqueará para siempre el intento de enviarte el informe si sigues adelante. Esto crearía una goroutine fuga. Por lo tanto, se está utilizando un canal de almacenamiento en búfer de 1 para evitar que esto suceda.

Conclusión

Es importante conocer y comprender los atributos de señalización en torno a las garantías, el estado del canal y el envío cuando se utilizan canales (o concurrencia). Le ayudarán a implementar el mejor comportamiento que necesita para los programas y algoritmos simultáneos que está escribiendo. Te ayudarán a encontrar errores y a detectar código potencialmente defectuoso.

En este post he compartido algunos programas de muestra que muestran cómo funcionan los atributos de señalización en diferentes escenarios. Hay excepciones a todas las reglas, pero estos patrones son una buena base para comenzar.

Revise estos esquemas como un resumen de cuándo y cómo pensar y usar canales de manera efectiva:

Mecánica del lenguaje

  • Utilice canales para orquestar y coordinar goroutines.
    • Enfóquese en los atributos de señalización y no en el intercambio de datos.
    • Señalización con o sin datos.
    • Cuestiona su uso para sincronizar el acceso al estado compartido.
      • Hay casos en los que los canales pueden ser más simples para esto, pero inicialmente cuestionan.
  • Canales sin búfer:
    • La recepción ocurre antes del Envío.
    • Beneficio: garantía del 100% de que se ha recibido la señal.
    • Coste: Latencia desconocida en el momento en que se recibirá la señal.
  • Canales en búfer:
    • El envío ocurre antes de la recepción.
    • Beneficio: Reduce la latencia de bloqueo entre la señalización.
    • Costo: No hay garantía cuando se ha recibido la señal.
      • Cuanto mayor sea el búfer, menor será la garantía.
      • El búfer de 1 puede darle un envío retrasado de garantía.
  • Canales de cierre:
    • El cierre ocurre antes de la Recepción (como el búfer).
    • Señalización sin datos.
    • Perfecto para señalar cancelaciones y plazos.
  • canales nulos: Bloque de envío y recepción
    • .
    • Apague la señalización
    • Perfecto para paradas de limitación de velocidad o a corto plazo.

Filosofía de diseño

  • Si un Envío dado en un canal PUEDE causar que la goroutine de envío se bloquee:
    • No se permite usar un canal con búfer mayor que 1.
      • Los tampones mayores que 1 deben tener motivos / mediciones.
    • Debo saber lo que pasa cuando el goroutine que envía bloquea.
  • Si un envío dado en un canal NO causa que la goroutine de envío se bloquee:
    • Tiene el número exacto de búferes para cada envío.
      • Patrón de separación en abanico
    • Tiene el búfer medido para la capacidad máxima.
      • patrón
  • Menos es más con los búferes.
    • No piense en el rendimiento cuando piense en búferes.Los búferes
    • pueden ayudar a reducir la latencia de bloqueo entre señales.
      • Reducir la latencia de bloqueo hacia cero no significa necesariamente un mejor rendimiento.
      • Si un búfer de uno le está dando un rendimiento lo suficientemente bueno, guárdelo.
      • Búferes de preguntas que son más grandes que uno y miden el tamaño.
      • Encuentre el búfer más pequeño posible que proporcione un rendimiento lo suficientemente bueno.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.