Goroutine/Channels

// just like a thread
go f(x, y, z)

// channels used to send data to different threads
ch <- v // v sends to ch
v := <-ch // v receives from ch

v, ok := <-ch // ok is false if there are no more values to receive and the channel is closed.

// buffered channel
ch := make(chan int, 100)
ch <- 1
💡
Sends to a buffered channel block only when the buffer is full. Receives block when the buffer is empty. It will cause fatal error: deadlock!

Using Goroutine/Channels to implement a worker pool

package main

import (
    "fmt"
    "time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for j := range jobs {
        fmt.Println("worker", id, "started  job", j)
        time.Sleep(time.Second)
        fmt.Println("worker", id, "finished job", j)
        results <- j * 2
    }
}

func main() {

    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs) // close jobs to indicate all jobs are sent to worker pool

    for a := 1; a <= numJobs; a++ {
        <-results
    }
}

Select

let a goroutine wait on multiple communication operations.
package main

import "fmt"

func fibonacci(c, quit chan int) {
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main() {
	c := make(chan int)
	quit := make(chan int)
	go func() {
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0 // 0 is sent to quit after the loop, by that time, c is always sent in fib
	}()
	fibonacci(c, quit)
}

Lock & Mutex

package main

import (
	"fmt"
	"sync"
	"time"
)

// SafeCounter is safe to use concurrently.
type SafeCounter struct {
	mu sync.Mutex
	v  map[string]int
}

// Inc increments the counter for the given key.
func (c *SafeCounter) Inc(key string) {
	c.mu.Lock()
	// Lock so only one goroutine at a time can access the map c.v.
	c.v[key]++
	c.mu.Unlock()
}

// Value returns the current value of the counter for the given key.
func (c *SafeCounter) Value(key string) int {
	c.mu.Lock()
	// Lock so only one goroutine at a time can access the map c.v.
	defer c.mu.Unlock()
	return c.v[key]
}

func main() {
	c := SafeCounter{v: make(map[string]int)}
	for i := 0; i < 1000; i++ {
		go c.Inc("somekey")
	}

	time.Sleep(time.Second)
	fmt.Println(c.Value("somekey"))
}
💡
We can also use defer to ensure the mutex will be unlocked as in the Value method. In Go language, defer statements delay the execution of the function or method or an anonymous method until the nearby functions returns.

Related Articles