// 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.