Go Concurrency: Goroutine and Channel, Select, Lock
Introduction
- Concurrency programming refers to the presence of multiple independent execution units in a program that can execute concurrently, improving the efficiency of the program. Go is known for its high concurrency, and its concurrency programming model is based on goroutines, which are lightweight threads that can create thousands of goroutines in a program without exhausting system resources.
- Go's
goroutineis a kind of coroutine, and the creation and destruction ofgoroutineare very fast. The scheduling ofgoroutineis automatically completed by the Go runtime system, and the programmer does not need to care about it. channelis a data structure used for communication between goroutines in Go language.channelis type-safesyncpackage provides some locking mechanisms, such assync.Mutex,sync.RWMutex,sync.WaitGroup, etc., which can ensure the safe access of shared resources.
Goroutine
- The format of Go language goroutine is as follows:
go
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}In the above code, the say function is a goroutine, go say("world") creates a goroutine, and say("hello") is the main goroutine.
Run the result
shell
hello
worldPerhaps this is not intuitive enough, we can output two strings multiple times in a loop to see the execution order of the goroutine.
go
func main() {
for i := 0; i < 5; i++ {
go say("world")
say("hello")
}
}Run the result
shell
hello
world
hello
hello
world
world
hello
world
hello
worldAs you can see, the execution order of the goroutine is not determined, because the scheduling of the goroutine is automatically completed by the Go runtime system.
Channel
- The channel in Go language is a type-safe data structure used for communication between goroutines.
Declaration
go
// var channel_name chan element_type
var intChan chan intCreate
- The creation format of a channel without a buffer is as follows:
go
// channel_name = make(chan element_type)
intChan = make(chan int)Send and Receive
- The format of sending and receiving channels is as follows:
go
// Send
// channel_name <- element
intChan <- 1
// Receive
// element = <- channel_name
i := <-intChanBuffer
- The buffer refers to the number of elements that can be stored in the channel. When the buffer is full, the send operation will block until there is space. When the buffer is empty, the receive operation will block until there are elements.
- The buffer size of the channel can be specified by the second parameter of the
makefunction, which is 0 by default, for example:
go
intChan = make(chan int, 10)Total Example
go
package main
import "fmt"
func routine1(c chan int) {
for i := 0; i < 10; i++ {
c <- i
}
close(c)
}
func routine2(c chan int) {
for {
i, ok := <-c
if !ok {
break
}
fmt.Println(i)
}
}
func main() {
c := make(chan int)
go routine1(c)
go routine2(c)
select {}
}In the above code, the routine1 function sends integers from 0 to 9 to the channel c, the routine2 function receives integers from the channel c and prints them, and the select{} is an empty select statement used to block the main goroutine.
Run the result
shell
0
1
2
3
4
5
6
7
8
9Select Statement
- The
selectstatement is used to handle multiple channel send and receive operations. Theselectstatement randomly selects an available channel to execute. If there is no available channel, it will block. - The format of the
selectstatement is as follows:
go
select {
case <-ch1:
// do something
case ch2 <- 1:
// do something
default:
// do something
}Lock
- The
syncpackage of Go language provides some locking mechanisms, such assync.Mutex,sync.RWMutex,sync.WaitGroup, etc., which can ensure the safe access of shared resources.
Here is an example using sync.Mutex:
go
package main
import (
"fmt"
"sync"
)
var count int
var lock sync.Mutex
func increment() {
lock.Lock()
defer lock.Unlock()
count++
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)x
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println(count)
}Extension
- In addition to using locks, you can also use the
atomicpackage to ensure the safe access of shared resources. This package ensures the atomicity of operations.
go
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var count int32
func increment() {
atomic.AddInt32(&count, 1)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
wg.Wait()
fmt.Println(count)
}Summary
- Go language's concurrency programming model is based on goroutines, which are lightweight threads. Reasonable use of goroutines can improve the efficiency of the program.
- Channels are data structures used for communication between goroutines in Go language. Channels are type-safe
- The
syncpackage provides some locking mechanisms, such assync.Mutex,sync.RWMutex,sync.WaitGroup, etc., which can ensure the safe access of shared resources. - The
atomicpackage can be used to ensure the safe access of shared resources. This package ensures the atomicity of operations.