By George Aristy / llorllale
go
followed by an expression (typically a function call):
func main() {
go func() {
fmt.Println("Hello goroutine!")
}()
}
A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type.- The Spec
make
keyword:
func main() {
unbuffered := make(chan int)
buffered := make(chan int, 3)
}
<-
operator:
// Which of the following compiles?
func main() {
readWrite := make(chan int)
readOnly := make(<-chan int)
writeOnly := make(chan<- int)
readWrite <- 1 // OK
<-readWrite // OK
readOnly <- 1 // compilation error
<-readOnly // OK
writeOnly <- 1 // OK
<-writeOnly // compilation error
}
range
keyword:
func main() {
ch := make(chan string, 3)
ch <- "Hello"
ch <- "Fellow"
ch <- "Gophers"
close(ch)
for s := range ch { // exits when channel is closed
fmt.Println(s)
}
// Output:
// Hello
// Fellow
// Gophers
}
select
statement:
select {
case <-ch1:
doSomething()
case <-ch2:
doSomethingElse()
...
case <-chN:
doTheNthThing()
}
select
example:
func main() {
rand.Seed(time.Now().Unix())
ch1 := make(chan int)
ch2 := make(chan int)
go func() { // write to ch1 after random delay
delay := time.Duration(rand.Intn(2)) * time.Second
time.Sleep(delay)
ch1 <- 1
close(ch1)
}()
go func() { // write to ch2 after random delay
delay := time.Duration(rand.Intn(2)) * time.Second
time.Sleep(delay)
ch2 <- 2
close(ch2)
}()
select { // Output: either 1 or 2
case v := <-ch1:
fmt.Println(v)
case v := <-ch2:
fmt.Println(v)
}
}
select
example:
func main() {
rand.Seed(time.Now().Unix())
ch1 := make(chan int)
ch2 := make(chan int)
go func() { // write to ch1 after random delay
delay := time.Duration(rand.Intn(2)) * time.Second
time.Sleep(delay)
ch1 <- 1
close(ch1)
}()
go func() { // write to ch2 after random delay
delay := time.Duration(rand.Intn(2)) * time.Second
time.Sleep(delay)
ch2 <- 2
close(ch2)
}()
select { // Output: either 1 or 2
case v := <-ch1:
fmt.Println(v)
case v := <-ch2:
fmt.Println(v)
}
}
select
example:
func main() {
rand.Seed(time.Now().Unix())
ch1 := make(chan int)
ch2 := make(chan int)
go func() { // write to ch1 after random delay
delay := time.Duration(rand.Intn(2)) * time.Second
time.Sleep(delay)
ch1 <- 1
close(ch1)
}()
go func() { // write to ch2 after random delay
delay := time.Duration(rand.Intn(2)) * time.Second
time.Sleep(delay)
ch2 <- 2
close(ch2)
}()
select { // Output: either 1 or 2
case v := <-ch1:
fmt.Println(v)
case v := <-ch2:
fmt.Println(v)
}
}
select
example:
func main() {
rand.Seed(time.Now().Unix())
ch1 := make(chan int)
ch2 := make(chan int)
go func() { // write to ch1 after random delay
delay := time.Duration(rand.Intn(2)) * time.Second
time.Sleep(delay)
ch1 <- 1
close(ch1)
}()
go func() { // write to ch2 after random delay
delay := time.Duration(rand.Intn(2)) * time.Second
time.Sleep(delay)
ch2 <- 2
close(ch2)
}()
select { // Output: either 1 or 2
case v := <-ch1:
fmt.Println(v)
case v := <-ch2:
fmt.Println(v)
}
}
func main() { // "main" goroutine
data := make(chan string) // data channel
go func() { // producer
for _, s := range []string{"Hello", "Fellow", "Gophers!"} {
data <- s
}
close(data)
}()
done := make(chan struct{}) // done channel
go func() { // consumer
for s := range data {
fmt.Println(s)
}
close(done)
}()
select {
case <-done:
fmt.Println("bye!")
case <-time.After(time.Second):
fmt.Println("timeout!")
}
}
func main() { // "main" goroutine
data := make(chan string) // data channel
go func() { // producer
for _, s := range []string{"Hello", "Fellow", "Gophers!"} {
data <- s
}
close(data)
}()
done := make(chan struct{}) // done channel
go func() { // consumer
for s := range data {
fmt.Println(s)
}
close(done)
}()
select {
case <-done:
fmt.Println("bye!")
case <-time.After(time.Second):
fmt.Println("timeout!")
}
}
func main() { // "main" goroutine
data := make(chan string) // data channel
go func() { // producer
for _, s := range []string{"Hello", "Fellow", "Gophers!"} {
data <- s
}
close(data)
}()
done := make(chan struct{}) // done channel
go func() { // consumer
for s := range data {
fmt.Println(s)
}
close(done)
}()
select {
case <-done:
fmt.Println("bye!")
case <-time.After(time.Second):
fmt.Println("timeout!")
}
}
func main() { // "main" goroutine
data := make(chan string) // data channel
go func() { // producer
for _, s := range []string{"Hello", "Fellow", "Gophers!"} {
data <- s
}
close(data)
}()
done := make(chan struct{}) // done channel
go func() { // consumer
for s := range data {
fmt.Println(s)
}
close(done)
}()
select {
case <-done:
fmt.Println("bye!")
case <-time.After(time.Second):
fmt.Println("timeout!")
}
}
func main() { // "main" goroutine
data := make(chan string) // data channel
go func() { // producer
for _, s := range []string{"Hello", "Fellow", "Gophers!"} {
data <- s
}
close(data)
}()
done := make(chan struct{}) // done channel
go func() { // consumer
for s := range data { // exits when data is closed
fmt.Println(s)
}
close(done)
}()
select {
case <-done:
fmt.Println("bye!")
case <-time.After(time.Second):
fmt.Println("timeout!")
}
// Output:
// Hello
// Fellow
// Gophers!
// bye!
}
func main() { // "main" goroutine
data := make(chan string) // data channel
go func() { // producer
for _, s := range []string{"Hello", "Fellow", "Gophers!"} {
data <- s
}
close(data)
}()
done := make(chan struct{}) // done channel
go func() { // consumer
for s := range data { // exits when data is closed
fmt.Println(s)
}
close(done)
}()
select {
case <-done:
fmt.Println("bye!")
case <-time.After(time.Second):
fmt.Println("timeout!")
}
// Output:
// Hello
// Fellow
// Gophers!
// bye!
}
sync
package
func main() { // "main" goroutine
var wg sync.WaitGroup
work := func(delay time.Duration) {
defer wg.Done() // signal this work is "done"
time.Sleep(delay)
}
wg.Add(3) // # of workers known beforehand
go work(time.Second)
go work(500 * time.Millisecond)
go work(700 * time.Millisecond)
wg.Wait() // blocks until all three are done
fmt.Println("done!")
}
func main() { // "main" goroutine
var wg sync.WaitGroup
work := func(delay time.Duration) {
defer wg.Done() // signal this work is "done"
time.Sleep(delay)
}
wg.Add(3) // # of workers known beforehand
go work(time.Second)
go work(500 * time.Millisecond)
go work(700 * time.Millisecond)
wg.Wait() // blocks until all three are done
fmt.Println("done!")
}
func main() { // "main" goroutine
var wg sync.WaitGroup
work := func(delay time.Duration) {
defer wg.Done() // signal this work is "done"
time.Sleep(delay)
}
wg.Add(3) // # of workers known beforehand
go work(time.Second)
go work(500 * time.Millisecond)
go work(700 * time.Millisecond)
wg.Wait() // blocks until all three are done
fmt.Println("done!")
}
func main() { // "main" goroutine
var wg sync.WaitGroup
work := func(delay time.Duration) {
defer wg.Done() // signal this work is "done"
time.Sleep(delay)
}
wg.Add(3) // # of workers known beforehand
go work(time.Second)
go work(500 * time.Millisecond)
go work(700 * time.Millisecond)
wg.Wait() // blocks until all three are done
fmt.Println("done!")
}
func main() { // "main" goroutine
var wg sync.WaitGroup
work := func(delay time.Duration) {
defer wg.Done() // signal this work is "done"
time.Sleep(delay)
}
wg.Add(3) // # of workers known beforehand
go work(time.Second)
go work(500 * time.Millisecond)
go work(700 * time.Millisecond)
wg.Wait() // blocks until all three are done
fmt.Println("done!")
}
func main() { // "main" goroutine
var wg sync.WaitGroup
work := func(delay time.Duration) {
defer wg.Done() // signal this work is "done"
time.Sleep(delay)
}
wg.Add(3) // # of workers known beforehand
go work(time.Second)
go work(500 * time.Millisecond)
go work(700 * time.Millisecond)
wg.Wait() // blocks until all three are done
fmt.Println("done!")
}
var once sync.Once
func expensiveInit() {
once.Do(func() {
fmt.Println("initialized!")
})
}
func main() {
expensiveInit() // prints "initialized!"
expensiveInit() // does nothing
expensiveInit() // does nothing
}
var once sync.Once
func expensiveInit() {
once.Do(func() {
fmt.Println("initialized!")
})
}
func main() {
expensiveInit() // prints "initialized!"
expensiveInit() // does nothing
expensiveInit() // does nothing
}
var once sync.Once
func expensiveInit() {
once.Do(func() {
fmt.Println("initialized!")
})
}
func main() {
expensiveInit() // prints "initialized!"
expensiveInit() // does nothing
expensiveInit() // does nothing
}
var once sync.Once
func expensiveInit() {
once.Do(func() {
fmt.Println("initialized!")
})
}
func main() {
expensiveInit() // prints "initialized!"
expensiveInit() // does nothing
expensiveInit() // does nothing
}
func printLetters(words []string) {
n := time.Duration(rand.Intn(100))
for _, s := range words {
fmt.Print(s + " ")
time.Sleep(n * time.Millisecond)
}
fmt.Println()
}
func main() {
rand.Seed(time.Now().Unix())
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
printLetters([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"})
}()
go func() {
defer wg.Done()
printLetters([]string{"k", "l", "m", "n", "o", "p", "q", "r", "s", "t"})
}()
wg.Wait()
}
func printLetters(words []string) {
n := time.Duration(rand.Intn(100))
for _, s := range words {
fmt.Print(s + " ")
time.Sleep(n * time.Millisecond)
}
fmt.Println()
}
func main() {
rand.Seed(time.Now().Unix())
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
printLetters([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"})
}()
go func() {
defer wg.Done()
printLetters([]string{"k", "l", "m", "n", "o", "p", "q", "r", "s", "t"})
}()
wg.Wait()
}
k a b c l d e m f g n h i o j
p q r s t
Char sequences are not on their own lines.
func printLetters(words []string) {
n := time.Duration(rand.Intn(100))
for _, s := range words {
fmt.Print(s + " ")
time.Sleep(n * time.Millisecond)
}
fmt.Println()
}
func main() {
rand.Seed(time.Now().Unix())
var wg sync.WaitGroup
var mu sync.Mutex
wg.Add(2)
go func() {
defer wg.Done()
mu.Lock() // acquire lock
defer mu.Unlock() // release lock when done
printLetters([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"})
}()
go func() {
defer wg.Done()
mu.Lock() // acquire lock
defer mu.Unlock() // release lock when done
printLetters([]string{"k", "l", "m", "n", "o", "p", "q", "r", "s", "t"})
}()
wg.Wait()
}
func printLetters(words []string) {
n := time.Duration(rand.Intn(100))
for _, s := range words {
fmt.Print(s + " ")
time.Sleep(n * time.Millisecond)
}
fmt.Println()
}
func main() {
rand.Seed(time.Now().Unix())
var wg sync.WaitGroup
var mu sync.Mutex
wg.Add(2)
go func() {
defer wg.Done()
mu.Lock() // acquire lock
defer mu.Unlock() // release lock when done
printLetters([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"})
}()
go func() {
defer wg.Done()
mu.Lock() // acquire lock
defer mu.Unlock() // release lock when done
printLetters([]string{"k", "l", "m", "n", "o", "p", "q", "r", "s", "t"})
}()
wg.Wait()
}
func printLetters(words []string) {
n := time.Duration(rand.Intn(100))
for _, s := range words {
fmt.Print(s + " ")
time.Sleep(n * time.Millisecond)
}
fmt.Println()
}
func main() {
rand.Seed(time.Now().Unix())
var wg sync.WaitGroup
var mu sync.Mutex
wg.Add(2)
go func() {
defer wg.Done()
mu.Lock() // acquire lock
defer mu.Unlock() // release lock when done
printLetters([]string{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j"})
}()
go func() {
defer wg.Done()
mu.Lock() // acquire lock
defer mu.Unlock() // release lock when done
printLetters([]string{"k", "l", "m", "n", "o", "p", "q", "r", "s", "t"})
}()
wg.Wait()
}
k l m n o p q r s t
a b c d e f g h i j
Char sequences guaranteed to be on their own lines.
sync/atomic
sync/atomic
sync/atomic
func main() {
var (
total int32
wg sync.WaitGroup
)
wg.Add(10_000)
for i := 0; i < 10_000; i++ {
go func() {
defer wg.Done()
total++
}()
}
wg.Wait()
fmt.Println(total) // example: 9106
}
sync/atomic
func main() {
var (
total int32
wg sync.WaitGroup
)
wg.Add(10_000)
for i := 0; i < 10_000; i++ {
go func() {
defer wg.Done()
atomic.AddInt32(&total, 1)
}()
}
wg.Wait()
fmt.Println(total) // 10000
}
map
guarded by a sync.RWMutex
should fulfill most use cases without sacrificing legibility.
func main() {
var m sync.Map
m.Store("key", 1)
m.Store(1, 1)
v, ok := m.Load("key")
if ok {
n := v.(int)
fmt.Println(n) // 1
}
v, ok = m.Load(1)
if ok {
n := v.(int)
fmt.Println(n) // 1
}
}
func main() {
timeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
}
var g sync.WaitGroup
g.Add(len(urls))
var client http.Client
var errors []error
var mu sync.Mutex
for _, url := range urls {
u := url
go func() {
defer g.Done()
req, err := http.NewRequestWithContext(timeout, http.MethodGet, u, nil)
if err != nil {
mu.Lock()
errors = append(errors, err)
mu.Unlock()
return
}
// Fetch the URL.
resp, err := client.Do(req)
if err != nil {
mu.Lock()
errors = append(errors, err)
mu.Unlock()
return
}
_ = resp.Body.Close()
}()
}
// Wait for all HTTP fetches to complete.
g.Wait()
if len(errors) == 0 {
fmt.Println("Successfully fetched all URLs.")
}
}
func main() {
timeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
}
g, _ := errgroup.WithContext(timeout)
var client http.Client
for _, url := range urls {
u := url
go func() {
g.Go(func() error {
req, err := http.NewRequestWithContext(timeout, http.MethodGet, u, nil)
if err != nil {
return err
}
// Fetch the URL.
resp, err := client.Do(req)
if err != nil {
return err
}
return resp.Body.Close()
})
}()
}
// Wait for all HTTP fetches to complete.
if err := g.Wait(); err == nil {
fmt.Println("Successfully fetched all URLs.")
}
}