Go的协程
什么是协程
协程是一种轻量级的线程,与线程相比,协程的创建和销毁的代价要小得多,协程的调度由用户控制,而线程的调度由操作系统控制。
协程的实现
Go语言的协程是由Go语言的运行时(runtime)调度完成的,而不是由操作系统调度,因此Go语言的协程也称为用户态线程或绿色线程。
协程是在用户态实现的,因此创建和销毁的代价要小得多,Go语言的协程调度也是由用户态的调度器完成的,因此调度的代价也要小得多。
协程的调度器是一个M:N调度器,M个协程对应N个操作系统线程,M个协程会被调度到N个操作系统线程上执行,当某个协程阻塞时,调度器会将其对应的操作系统线程让出,让其他协程继续执行。
协程的使用
Go语言的协程是由go关键字创建的,go关键字后面跟着一个函数调用,这个函数调用就是一个协程,这个协程会在一个新的goroutine中执行。
package main
import "fmt"
import "time"
func main() {
go func() {
fmt.Println("Hello, World!")
}()
time.Sleep(time.Second)
}
上面的代码中,main函数中的go func() {...}()就是一个协程,这个协程会在一个新的goroutine中执行,main函数会在主goroutine中执行,主goroutine会等待新的goroutine执行完毕后再退出。
协程的通信
协程之间的通信可以通过共享内存来实现,也可以通过通道来实现。
共享内存
协程之间可以通过共享内存来通信,共享内存的方式有很多种,比如全局变量、共享内存区等。
package main
import "fmt"
var done bool
func main() {
go func() {
done = true
}()
for !done {
fmt.Println("Hello, World!")
}
}
上面的代码中,main函数中的go func() {...}()就是一个协程,这个协程会在一个新的goroutine中执行,main函数会在主goroutine中执行,主goroutine会不断地打印Hello, World!,直到done变量的值为true,done变量的值为true时,主goroutine退出,程序结束。
通道
协程之间可以通过通道来通信,通道是一种特殊的数据结构,它可以在多个协程之间传递数据。
package main
import "fmt"
func main() {
done := make(chan bool)
go func() {
done <- true
}()
<-done
fmt.Println("Hello, World!")
}
上面的代码中,main函数中的go func() {...}()就是一个协程,这个协程会在一个新的goroutine中执行,main函数会在主goroutine中执行,主goroutine会从done通道中读取数据,当done通道中有数据时,主goroutine会退出,程序结束。
协程与线程的区别
协程与线程的区别主要有以下几点:
- 协程是由用户态的调度器调度的,线程是由操作系统调度的。
- 协程的创建和销毁的代价要小得多,协程的调度的代价也要小得多。
- 协程之间的通信可以通过共享内存来实现,也可以通过通道来实现,线程之间的通信只能通过共享内存来实现。
- 协程的调度是由用户控制的,线程的调度是由操作系统控制的。
- 协程的调度器是一个
M:N调度器,M个协程对应N个操作系统线程,M个协程会被调度到N个操作系统线程上执行,当某个协程阻塞时,调度器会将其对应的操作系统线程让出,让其他协程继续执行,线程的调度器是一个1:1调度器,1个线程对应1个操作系统线程,当某个线程阻塞时,调度器会将其对应的操作系统线程让出,让其他线程继续执行。 - 协程的调度器是抢占式的,线程的调度器是非抢占式的