Go-协程(Goroutine)
Goroutine是什么?
Go协程是与其它函数或方法一起并发运行的函数或方法。Go协程可以看作是轻量级线程,与线程相比,创建一个Go协程的成本很小。因此在Go应用中,常常会看到有数以千计的Go协程并发运行。
Goroutine相对于线程的优势
- 相对线程而言,Go协程的成本极低,堆栈大小只有若干kb,并且根据应用的需求进行增减。而进程必须指定堆栈大小,其堆栈是固定不变的。
- Go协程会复用数量更少的OS线程。即使程序有数以千计的Go协程,也可能只有一个线程。如果该线程中的某一个Go协程发生了阻塞(比如等待用户输入),那么系统会再创建一个OS线程,并把其余Go协程都移动到这个新的OS线程,所有这一切都在运行时进行,作为程序员我们没有直接面临这些复杂的细节,而是有一个简洁的API来处理并发。
- Go协程使用信道(Channel)来进行通信,信道用于防止多个协程访问共享内存时发生竞争态条件(Race Condition)。信道可以看作是Go协程之间通信的管道。
如何启动一个Goroutine?
Go启动协程非常简单,只需要在调用函数或方法时,在前面加上关键字 go ,既可以让一个新的Go协程并发地运行。
package main import( "fmt" "time" ) func calc() { fmt.Println("hello") time.Sleep(3 * time.Second) fmt.Println("hello end") // 这一行为什么不会打印?因为主协程已经执行完毕了。 } func main() { // 如果协程还没创建,主协程就结束了,协程也无法运行。 go calc() time.Sleep(1 * time.Second) // 这里睡眠一秒让协程有时间运行起来 fmt.Println("main function") }
- 启动一个新的协程时,协程调用会立即返回。与函数不同,程序控制不会去等待Go协程执行完毕。在调用Go协程之后,程序控制会立即执行到代码的下一行,而忽略该协程的任何返回值。
- 如果希望运行其它Go协程,Go主协程必须继续运行着。如果Go主协程终止,其它Go协程也不会继续运行下去,即使已经开始运行。
启动多个Goroutine
package main import ( "fmt" "time" ) func numbers() { for i := 1; i <= 5; i++ { time.Sleep(250 * time.Millisecond) // 1250 fmt.Printf("%d ", i) } } func alphabets() { for i := 'a'; i <= 'e'; i++ { time.Sleep(400 * time.Millisecond) // 2000 fmt.Printf("%c ", i) } } func main() { go numbers() go alphabets() time.Sleep(2100 * time.Millisecond) //主协程总运行时间必须大于某个子协程的总执行时间。 fmt.Println("main terminated") }
Goroutine其它操作
线程是由操作系统进行管理的处于内核态,线程之间进行切换,会发生用户态到内核态的切换。当系统中运行大量线程时,系统就会频繁切换,由此系统运行就会变慢、而用户态的线程,支持大量线程创建,也叫协程或者Goroutine。
package main
import (
"fmt"
"runtime"
"time"
)
func calc() {
var i int
for {
i++
}
}
func main() {
// 多核控制
cpu := runtime.NumCPU() // 查看cpu核数
fmt.Println("cpu: ", cpu)
runtime.GOMAXPROCS(cpu) // 代码使用cpu核数
/* 默认使用所有cpu */
for i := 0; i < 10; i++ {
go calc()
}
time.Sleep(time.Hour)
}

浙公网安备 33010602011771号