Channels

Channels designed to work with goroutines. They are strict typed. Can only send and receive items of same type as defined with make

package main

import (
    "fmt"
    "sync"
)

var wg = sync.WaitGroup{}

func main() {
    ch := make(chan int)
    wg.Add(2)

    go func() {
        defer wg.Done()
        i := <- ch
        fmt.Println(i)
    }()

    go func() {
        defer wg.Done()
        i := 42
        ch <- i
        i = 27
    }()

    wg.Wait()

    // Output:
    // 42
}

Example of read-only and write-only channels, where <- sign shows data flow

package main

import (
    "fmt"
    "sync"
)

var wg = sync.WaitGroup{}

func main() {
    ch := make(chan int)
    wg.Add(2)
    // example of read-only channel
    go func(ch <-chan int) {
        i := <-ch
        fmt.Println(i)
        wg.Done()
    }(ch)

    // this is write-only channel
    go func(ch chan<- int) {
        ch <- 42
        wg.Done()
    }(ch)

    wg.Wait()

    // Output:
    // 42
}

By default channels block sender side till receiver available and block receiver side till message is available. To decouple sender and receiver buffered channels are used Buffered channel example

func main() {
    ch := make(chan int, 50)
    wg.Add(2)
    go func(ch <-chan int) {
        i := <-ch
        fmt.Println(i)
        i := <-ch
        wg.Done()
    }(ch)

    go func(ch chan<- int) {
        ch <- 42
        ch <- 27
        wg.Done()
    }(ch)

    wg.Wait()

    // Output:
    // 42
    // 27
}

Close channel to signal other goroutines that channel wouldn't receive more messages. That will help to finish the loop When channel was closed, nothing can change it(no reopen, resend etc)

func main() {
    ch := make(chan int, 50)
    wg.Add(2)
    go func(ch <-chan int) {
        for i := range ch {
            fmt.Println(i)
        }
        wg.Done()
    }(ch)

    go func(ch chan<- int) {
        ch <- 1
        ch <- 2
        ch <- 3
        close(ch)
        wg.Done()
    }(ch)

    wg.Wait()
    // Output:
    // 1
    // 2
    // 3
}

Retrieving value from channel returns 2 values like map item retrieving

for {
    // here is ok will be true if channel is open and value received and false if channel was closed
    if i, ok := <- ch; ok {
        fmt.Println(i)
    } else {
        break
    }
}

Empty struct is not allocating memory. So channel with empty struct is called signal only channel. var doneCh = make(chan struct{}). Example of signal with select statement:

package main

import (
    "fmt"
    "time"
)

var ch = make(chan int, 50)
var doneCh = make(chan struct{})

func main() {
    go printFromChannel()
    ch <- 1
    ch <- 2
    // Let goroutine run
    time.Sleep(100 * time.Millisecond)
    doneCh <- struct{}{}
}

func printFromChannel() {
    for {
        select {
        case number := <-ch:
            fmt.Println(number)
        case <-doneCh:
            break
        }
    }
}