GO 并发编程
协程(进程 线程)
- 进程是“程序执行的一个实例” ,担当分配系统资源的实体。进程创建必须分配一个完整的独立地址空间。进程切换只发生在内核态。
- 线程:线程是进程的一个执行流,独立执行它自己的程序代码。
- 协程:协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。在语言级别可以创建并发协程,然后编写代码去进行管理。go将这一步承包下来,使协程并发运行成本更低。
func main() {
http.HandleFunc("/next", handler)
// func这个函数会是以协程的方式运行。这样就可以提供程序的并发处理能力
go func() {
for i := 0; ; i++ {
nextID <- i
}
}()
http.ListenAndServe("localhost:8080", nil)
}
goruntime
参考goruntime详解,操作系统对cpu有自己的scheduler方案,如任务A在执行完后,选择哪个任务来执行,使得某个因素(如进程总执行时间,或者磁盘寻道时间等)最小,达到最优的服务。
Go有自己的scheduler,语言级别实现了并发。
每一个Go程序都附带一个runtime,runtime负责与底层操作系统交互,也都会有scheduler对goruntines进行调度。在scheduler中有三个非常重要的概念:P,M,G。详情后续再写。
# Goroutine scheduler
# The scheduler's job is to distribute ready-to-run goroutines over worker threads.
#
# The main concepts are:
# G - goroutine.
# M - worker thread, or machine.
# P - processor, a resource that is required to execute Go code.
# M must have an associated P to execute Go code, however it can be
# blocked or in a syscall w/o an associated P.
#
# Design doc at https://golang.org/s/go11sched.
runtime包与goroutime
尽管 Go 编译器产生的是本地可执行代码,这些代码仍旧运行在 Go 的 runtime(这部分的代码可以在 runtime 包中找到)当中。这个 runtime 类似 Java 和 .NET 语言所用到的虚拟机,它负责管理包括内存分配、垃圾回收(第 10.8 节)、栈处理、goroutine、channel、切片(slice)、map 和反射(reflection)等等。
-
Gosched
:让当前线程让出 cpu 以让其它线程运行,它不会挂起当前线程,因此当前线程未来会继续执行 -
NumCPU
:返回当前系统的 CPU 核数量 -
GOMAXPROCS
:设置最大的可同时使用的 CPU 核数 -
Goexit
:退出当前 goroutine(但是defer语句会照常执行) -
NumGoroutine
:返回正在执行和排队的任务总数 -
GOOS
:目标操作系统
NumCPU
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Println("cpus:", runtime.NumCPU())
fmt.Println("goroot:", runtime.GOROOT())
fmt.Println("archive:", runtime.GOOS)
// 4
// /usr/local/golang
// linux
}
GOMAXPROCS
package main
import (
"fmt"
"runtime"
)
func init() {
runtime.GOMAXPROCS(1)
}
func main() {
// 任务逻辑...
}
Golang 默认所有任务都运行在一个 cpu 核里,如果要在 goroutine 中使用多核,可以使用 runtime.GOMAXPROCS 函数修改,当参数小于 1 时使用默认值。
Gosched
这个函数的作用是让当前 goroutine 让出 CPU,当一个 goroutine 发生阻塞,Go 会自动地把与该 goroutine 处于同一系统线程的其他 goroutine 转移到另一个系统线程上去,以使这些 goroutine 不阻塞。
package main
import (
"fmt"
"runtime"
)
func init() {
runtime.GOMAXPROCS(1) # 使用单核
}
func main() {
exit := make(chan int)
go func() {
defer close(exit)
go func() {
fmt.Println("b")
}()
}()
for i := 0; i < 4; i++ {
fmt.Println("a:", i)
if i == 1 {
runtime.Gosched() #切换任务
}
}
<-exit
}
# 运行结果
# a: 0
# a: 1
# b:
# a:2
# a: 3
channel
channel是Go语言在语言级别提供的goroutine间的通信方式。我们可以使用channel在两个或 多个goroutine之间传递消息。
channel 会某种情况下出现阻塞,通过控制channel的阻塞来管理协程的并发与流程控制。
有疑问加站长微信联系(非本文作者)