Skip to content

Latest commit

 

History

History
141 lines (106 loc) · 5.03 KB

File metadata and controls

141 lines (106 loc) · 5.03 KB

Understanding Golang Channels

Golang channels are a powerful feature for goroutine communication.
They provide a way for goroutines to synchronize and pass data.

drawing

Table of Contents

  1. Introduction to Golang Channels
  2. Creating and Using Channels
  3. Channel Directions
  4. Buffered and Unbuffered Channels
  5. Select Statement
  6. Common Pitfalls and Issues
  7. Best Practices
  8. Resources

Introduction

Golang channels are typed conduits through which you can send and receive values using the channel operator, <-.

Creating and Using Channels

ch := make(chan int)  // Create a new channel of type int
ch <- 5  // Send a value into the channel
value := <-ch  // Receive a value from the channel
value, ok := <-ch // Receive a value from channel ch; ok is false if ch is closed and contains no more values.

Channel Directions

You can specify a direction on a channel type, restricting it to either sending or receiving values.

var sendCh chan<- int  // A channel for sending integers
var recvCh <-chan int  // A channel for receiving integers

Buffered and Unbuffered Channels

Buffered channels have a capacity, whereas unbuffered channels do not.

ch := make(chan int) // Create an unbuffered channel
ch := make(chan int, 3) // Create a buffered channel with a capacity of 3

Select Statement

The select statement lets a goroutine wait on multiple communication operations.

select {
case msg1 := <-ch1:  // Receiving from channel ch1
    fmt.Println("received", msg1)
case msg2 := <-ch2:  // Receiving from channel ch2
    fmt.Println("received", msg2)
default:  // Default case, executed if no other cases are ready
    fmt.Println("no message received")
}

Common Pitfalls and Issues

  • Deadlocks:

    • Deadlocks occur when goroutines are waiting on each other indefinitely due to improper use of channels, often leading to the program hanging.
  • Starvation:

    • Starvation happens when one or more goroutines never get access to a channel because other goroutines are constantly using it.
  • Livelock:

    • Livelocks are similar to deadlocks, except the goroutines continue to execute without making any progress because they keep retrying an operation that will never succeed due to a mutual condition that is never met.

Panic Conditions

  • Sending on a Closed Channel:
    • A panic occurs if you send on a closed channel.
ch := make(chan int)
close(ch)
ch <- 5 // Panic: send on closed channel
  • Closing a Closed Channel:
    • Closing an already closed channel also causes a panic.
ch := make(chan int)
close(ch)
close(ch) // Panic: close of closed channel
  • Closing a Nil Channel:
    • Attempting to close a nil channel will result in a panic.
var ch chan int
close(ch) // Panic: close of nil channel
  • Accessing Channels After Panic:
    • If a panic occurs when sending or receiving on a channel, it's crucial to handle the panic appropriately;
      otherwise, subsequent operations on the channel may have undefined behavior.

Examples and tests

See package

Best Practices

  • Channel Ownership:
    • It's strongly recommended that the producer of a channel should have ownership and be responsible for closing the channel.
      This practice helps prevent panics from multiple attempts to close a channel.
  • Comma-Ok Idiom:
    • Always use the comma-ok idiom when receiving from channels to check if the channel is closed.
      The comma-ok idiom helps in safely checking the state of the channel and prevents potential issues that could arise from reading from closed channels.
  • Use Context for Cancellation Signals:
    • Instead of closing channels to signal cancellation, it's advisable to use the context package.
      The context package provides a standardized mechanism for cancellation signals, deadlines, and passing request-scoped data.
      This makes your code more idiomatic and easier to work with, especially in larger codebases or libraries.
  • Optimize Large Data Transfers:
    • Sending large objects or structs on a channel can cause performance issues as data is sent by value, which involves copying the data.
      This can increase memory usage and slow down the program, especially in high-throughput or low-latency systems.
      To mitigate this, consider (carefully) sending pointers to data instead of the data itself.
      This way, only the pointer is copied, which is much more efficient.

Resources