This is a creation in Article, where the information may have evolved or changed.
Goroutine
Go provides support for concurrent programming at the language level and is implemented using lightweight threading (co-process). You can create a concurrent execution unit by simply adding the GO keyword before the function call statement. The developer does not need to know any execution details, and the scheduler will automatically schedule it to be executed on the appropriate system thread. Goroutine is a very lightweight implementation that can perform thousands of concurrent tasks in a single process. In fact, the entry function, main, runs as Goroutine. There is also a matching channel type that implements the CSP mode of "sharing memory with communication".
go func() { println("Hello, World!")}
The scheduler does not guarantee multiple Goroutine execution orders, and the process exits without waiting for them to end. By default, only one system thread is allowed to serve goroutine after a process is started. You can use environment variables or standard function runtime. Gomaxprocs modified (Go 1.5 default). Let the altimeter implement multi-core parallelism, not just concurrency, with multiple threads.
func sum(id int) { var x int64 for i := 0; i < math.MaxUint32; i++ { x += int64(i) } println(id, x)}func main() { wg := new(sync.WaitGroup) wg.Add(2) for i := 0; i < 2; i++ { go func(id int) { defer wg.Done() sum(id) }(i) } wg.Wait()}输出:$ go build -o test$ time -p ./test0 92233720304123248651 9223372030412324865real 7.70 // 程序开始到结束时间差 (非非 CPU 时间)user 7.66 // 用用户态所使用用 CPU 时间片片 (多核累加)sys 0.01 // 内核态所使用用 CPU 时间片片$ GOMAXPROCS=2 time -p ./test0 92233720304123248651 9223372030412324865real 4.18user 7.61// 虽然总时间差不多,但由 2 个核并行行,real 时间自自然少了许多。sys 0.02
Call runtime. Goexit will immediately terminate the current goroutine execution, and the scheduler ensures that all registered defer deferred calls are executed.
func main() { wg := new(sync.WaitGroup) wg.Add(1) go func() { defer wg.Done() defer println("A.defer") func() { defer println("B.defer") runtime.Goexit() // 终止止当前 goroutine println("B") // 不会执行行 }() println("A") // 不会执行行 }() wg.Wait()}//输出:B.deferA.defer
Similar to the gosched yield function, the underlying thread is conceded, the current goroutine is paused, and the queue is placed back to wait for the next scheduled execution.
func main() { wg := new(sync.WaitGroup) wg.Add(2) go func() { defer wg.Done() for i := 0; i < 6; i++ { println(i) if i == 3 { runtime.Gosched() } } }() go func() { defer wg.Done() println("Hello, World!") }() wg.Wait()}//输出$ go run main.go0123Hello, World!45
Channel
Reference type channel is a concrete implementation of the CSP model for communication between multiple goruntine. Synchronization is implemented internally to ensure concurrency security. The default is synchronous mode, which requires sending and receiving pairing. Otherwise it will be blocked until the other party is ready and awakened.
func main() { data := make(chan int) // 数据交换队列 exit := make(chan bool) // 退出通知 go func() { for d := range data {// 从队列迭代接收数据,直到 close 。 fmt.Println(d) } fmt.Println("recv over.") exit <- true// 发出退出通知。 }() data <- 1// 发送数据。 data <- 2 data <- 3 close(data)// 关闭队列。 fmt.Println("send over.") <-exit// 等待退出通知。}//输出:123send over.recv over.
Asynchronously determines whether a block is blocked by judging the buffer. If the buffer is full, the send is blocked, the buffer is empty, and the receive is blocked. In general, asynchronous channel can reduce queueing congestion and be more efficient. However, you should consider using pointers to circumvent large object copies, packaging multiple elements, reducing buffer size, and so on.
func main() { data := make(chan int, 3)// 缓冲区可以存储 3 个元素 exit := make(chan bool) data <- 1// 在缓冲区未满前,不会阻塞。 data <- 2 data <- 3 go func() { for d := range data {// 在缓冲区未空前,不会阻塞。 fmt.Println(d) } exit <- true }() data <- 4// 如果缓冲区已满,阻塞。 data <- 5 close(data) <-exit}
Buffers are internal properties, not type-forming features.
var a, b chan int = make(chan int), make(chan int, 3)
In addition to using range, you can also use the OK-IDIOM mode to determine if the channel is closed.
for { if d, ok := <-data; ok { fmt.Println(d) } else { break }}
Sending data to the closed channel throws a panic error, and the receive immediately returns a value of 0. The nil channel, whether sent or received, will be blocked. The built-in function Len returns the number of buffered elements that have not been read, and the CAP returns the buffer size.
Unidirectional
The channel can be implicitly converted to a single queue, only to be received or only sent.
c := make(chan int, 3)var send chan <- int = c // send onlyvar recv <- chan int = c // receiver only
Choose
If you need to process multiple channel simultaneously, you can use the SELECT statement, which randomly selects an available channel for sending and receiving operations, or executes the default case.
func main() { a, b := make(chan int, 3), make(chan int) go func() { v, ok, s := 0, false, "" for { select {// 随机选择可用用 channel,接收数据。 case v, ok = <-a: s = "a" case v, ok = <-b: s = "b" } if ok { fmt.Println(s, v) } else { os.Exit(0) } } }() for i := 0; i < 5; i++ { select {// 随机选择可用用 channel,发送数据。 case a <- i: case b <- i: } } close(a) select {}// 没有可用用 channel,阻塞 main goroutine。}//输出:b 3a 0a 1a 2b 4
Mode
Package concurrent tasks and channel with simple Factory mode.
func NewTest() chan int { c := make(chan int) rand.Seed(time.Now().UnixNano()) go func() { time.Sleep(time.Second) c <- rant.Int() }() return c}func main() { t := NewTest() println(<-t) //等待gorountime结束返回。}
The signal volume is realized by channel (semaphore).
func main() { wg := sync.WaitGroup{} wg.Add(3) sem := make(chan int, 1) for i := 0; i < 3; i++ { go func(id int) { defer wg.Done() sem <- 1 // 向 sem 发送数据,阻塞或者成功。 for x := 0; x < 3; x++ { fmt.Println(id, x) } <- sem // 接收数据,使得其他阻塞 goroutine 可以发送数据。 }(i) } wg.Wait()}
Use the closed channel to issue an exit notification.
func main() { var wg sync.WaitGroup quit := make(chan bool) for i := 0; i < 2; i++ { wg.Add(1) go func(id int) { defer wg.Done() task := func() { println(id, time.Now().Nanosecond()) time.Sleep(time.Second) } for { select { case <- quit: // closed channel 不会阻塞,因此可用作退出通知。 return default://执行正常任务 task() } } }(i) } time.Sleep(time.Second * 5) // 让测试 goroutine 运行一会。 close(quit) // 发出退出通知。 wg.Wait()}
Time-Out (timeout) is implemented with select. The channel is the first class of objects, which can be passed as arguments (internally implemented as pointers) or as struct members.
type Request struct {data []int ret chan int}func newrequest (data ... int) *request {retur n &request{data, make (chan int, 1)}}func Process (req *request) {x: = 0 for _, I: = Range Req.data {x + = i} Req.ret <-X}func main () {req: = Newrequest (+), Process (req) fmt. Println (<-req.ret)}