并发编程
Go语言在语言层面上就支持了并行,虽然一般来说,程序会被编写为顺序执行的,因为这样程序写起来很简单,逻辑也很简单,而且还很容易维护,但其实在某些情况下,并行执行多个任务会带来更大的优势。
Go语言中里的并发指的是能让某个函数独立于其他函数运行的能力,当一个函数创建为协程goroutine
时,Go语言会将其视为一个独立的工作单元,这个单元会被调度到可用调度到可用的逻辑处理器上执行。
并发与并行
在了解并发编程之前,我们需要先明白什么是操作系统的线程'thread'和进程'process':
当运行一个程序的时候,操作系统会为这个应用程序启动一个进程,而一个线程是一个执行空间,在进程当中可以包括有多个线程,最初始的线程被称为主线程
操作系统是在物理处理器上调度线程来运行的,而Go语言在运行时会在逻辑处理器上调度goroutine
来运行。
而且,goroutine
是非常强大的,举个例子:
正在运行的goroutine
需要执行一个阻塞的系统调用,比如打开一个文件,当这类发生调用的时候,线程和goroutine
会从逻辑处理器上分离,该线程会继续阻塞,等待系统调用的返回。一旦被阻塞的系统调用执行完成并返回,对应的goroutine
会放回到本地运行队列,而之前的线程会保存好,以便之后可以继续使用。
但并发concurrency
和并行parallelism
又是完全不同的两个概念,并行是让不同的代码片段同时在不同的物理处理器上执行。
并行的关键是同时做很多事情,而并发是指同时管理很多事情
接下来就一起领略一下Go语言并发编程的魅力所在吧!
制定使用核心数
Go语言默认会调用CPU核心数,这些功能都是自动调整的,但也提供了相应的标准库---flags
,如下
var numCores = flag.Int("n", 2, "CPU核心数")
写个简单的程序:
package main
import (
"fmt"
"time"
)
func main() {
fmt.Println("Start")
go longWait()
go shortWait()
fmt.Println("挂起MAIN()")
time.Sleep(10 * time.Second)
fmt.Println("End")
}
func longWait() {
fmt.Println("LongWait() Starting...")
time.Sleep(5 * time.Second)
fmt.Println("LongWait() Ending...")
}
func shortWait() {
fmt.Println("ShortWait() Starting...")
time.Sleep(2 * time.Second)
fmt.Println("ShortWait() Ending...")
}
/** The result is:
Start
挂起MAIN()
LongWait() Starting...
ShortWait() Starting...
ShortWait() Ending...
LongWait() Ending...
End
*/
整个函数的执行过程很简单,就是一个简单的并行运行,然后每一个函数都在运行的开始和结束阶段输出了消息,为了模拟它们的运算时间消耗,使用time.Sleep()
函数进行模拟。而且如果我们使用计时函数进行观察的话,我们还可以发现,整个程序的运行时间就是main()
当中的10s,这都归功于go
这一个关键词,它可以让两个函数并发的执行,我们可以尝试着把go
去掉,那么我们可以发现运行的结果的顺序与时间也是与上面的完全不同的:
Start
LongWait() Starting...
LongWait() Ending...
ShortWait() Starting...
ShortWait() Ending...
挂起MAIN()
End
The last time is: 17.0004881s
这样,我们就对并发编程有了一个初步的认识,下一次我们将会介绍一下协程又是如何实现的。
网友评论