在上一篇中,我们讨论了并发,以及并发和并行的区别。在这篇教程中我们将讨论在Go中如何通过Go协程实现并发。

什么是协程

Go协程(Goroutine)是与其他函数或方法同时运行的函数或方法。可以认为Go协程是轻量级的线程。与创建线程相比,创建Go协程的成本很小。因此在Go中同时运行上千个协程是很常见的。

Go协程对比线程的优点
  • 与线程相比,Go协程的开销非常小。Go协程的堆栈大小只有几kb,它可以根据应用程序的需要而增长和缩小,而线程必须指定堆栈的大小,并且堆栈的大小是固定的。
  • Go协程被多路复用到较少的OS线程。在一个程序中数千个Go协程可能只运行在一个线程中。如果该线程中的任何一个Go协程阻塞(比如等待用户输入),那么Go会创建一个新的OS线程并将其余的Go协程移动到这个新的OS线程。所有这些操作都是 runtime 来完成的,而我们程序员不必关心这些复杂的细节,只需要利用 Go 提供的简洁的 API 来处理并发就可以了。
  • Go 协程之间通过信道(channel)进行通信。信道可以防止多个协程访问共享内存时发生竟险(race condition)。信道可以想象成多个协程之间通信的管道。我们将在下一篇教程中介绍信道。
如何创建一个协程?
go

让我们创建一个协程:

package main

import (
"fmt"
) func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
fmt.Println("main function")
}
go hello()hello()main()main

运行这个程序,你将得到一个惊喜。

main function
  • 当创建一个Go协程时,创建这个Go协程的语句立即返回。与函数不同,程序流程不会等待Go协程结束再继续执行。程序流程在开启Go协程后立即返回并开始执行下一行代码,忽略Go协程的任何返回值。
  • 在主协程存在时才能运行其他协程,主协程终止则程序终止,其他协程也将终止。
go hello()hellomain functionhello

让我们修复这个问题:

package main

import (
"fmt"
"time"
) func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
time.Sleep( * time.Second)
fmt.Println("main function")
}
go hello()Hello world goroutinemain function

在主协程中使用 Sleep 函数等待其他协程结束的方法是不正规的,我们用在这里只是为了说明Go协程是如何工作的。信道可以用于阻塞主协程,直到其他协程执行完毕。我们将在下一篇教程中讨论信道。

开启多个协程

让我们写一个程序开启多个协程来更好的理解协程:

package main

import (
"fmt"
"time"
) func numbers() {
for i := ; i <= ; i++ {
time.Sleep( * time.Millisecond)
fmt.Printf("%d ", i)
}
}
func alphabets() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep( * time.Millisecond)
fmt.Printf("%c ", i)
}
}
func main() {
go numbers()
go alphabets()
time.Sleep( * time.Millisecond)
fmt.Println("main terminated")
}
numbersalphabetsnumbersalphabets

程序的输出为:

 a   b  c  d e main terminated

下面的图片描述了这个程序是如何工作的

numbersalphabets250ms500ms1 a 2 3 b 4 c 5 d e main terminated

本文转自:https://blog.csdn.net/u011304970/article/details/75096323?locationNum=3&fps=1