1. 概念
Go 协程可以看作是轻量级线程。与线程相比,创建一个 Go 协程的成本很小。
2. 协程的优势
1)go协程只需要极少的栈内存(大概4~5KB),默认情况下,线程栈的大小为1MB。
2)go协程也叫用户态线程,协程之间的切换发生在用户态。在用户态没有时钟中断、系统调用等机制,因此效率高。
3)go 协程会复用(Multiplex)数量更少的 OS 线程。即使程序有数以千计的 Go 协程,也可能只有一个线程。如果该线程中的某一 Go 协程发生了阻塞(比如说等待用户输入),那么系统会再创建一个 OS 线程,并把其余 Go 协程都移动到这个新的 OS 线程。这一切都在运行时进行。
4)go 协程使用通道(Channel)来进行通信。信道用于防止多个协程访问共享内存时发生竞态条件(Race Condition)。
3. 单个Go协程
package main import ( "fmt" ) func test() { fmt.Println("Hello go") } func main() { go test() fmt.Println("End") }
运行结果:
End
结果分析:
1)启动一个新的协程时,协程的调用会立即返回。与函数不同,程序控制不会去等待 Go 协程执行完毕。在调用 Go 协程之后,程序控制会立即返回到代码的下一行,忽略该协程的任何返回值。
2)如果希望运行其他 Go 协程,Go 主协程必须继续运行着。如果 Go 主协程终止,则程序终止,于是其他 Go 协程也不会继续运行。
修改:
package main import ( "fmt" "time" ) func test() { fmt.Println("Hello go") } func main() { go test() time.Sleep(1 * time.Second) fmt.Println("End") }
运行结果:
Hello go
End
结果分析:
将主协程休眠1秒,子协程将有足够的足够的时间来执行。
4. 多个Go协程
package main import ( "fmt" "time" ) func numbers() { for i := 1; i <= 5; i++ { time.Sleep(250 * time.Millisecond) fmt.Printf("%d ", i) } } func alphabets() { for i := 'a'; i <= 'e'; i++ { time.Sleep(400 * time.Millisecond) fmt.Printf("%c ", i) } } func main() { go numbers() go alphabets() time.Sleep(3000 * time.Millisecond) fmt.Println("main terminated") }
执行结果:
1 a 2 3 b 4 c 5 d e main terminated
一张图解析:
5. 说明
go示例代码已上传至github: https://github.com/Lucas2525117/go_prac