Go的协程

94 阅读3分钟

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变量的值为truedone变量的值为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会退出,程序结束。

协程与线程的区别

协程与线程的区别主要有以下几点:

  1. 协程是由用户态的调度器调度的,线程是由操作系统调度的。
  2. 协程的创建和销毁的代价要小得多,协程的调度的代价也要小得多。
  3. 协程之间的通信可以通过共享内存来实现,也可以通过通道来实现,线程之间的通信只能通过共享内存来实现。
  4. 协程的调度是由用户控制的,线程的调度是由操作系统控制的。
  5. 协程的调度器是一个M:N调度器,M个协程对应N个操作系统线程,M个协程会被调度到N个操作系统线程上执行,当某个协程阻塞时,调度器会将其对应的操作系统线程让出,让其他协程继续执行,线程的调度器是一个1:1调度器,1个线程对应1个操作系统线程,当某个线程阻塞时,调度器会将其对应的操作系统线程让出,让其他线程继续执行。
  6. 协程的调度器是抢占式的,线程的调度器是非抢占式的