go语言实战读书笔记之 goroutine
go语言并发: 能让某个函数独立于其他函数运行的能力
当一个函数创建为goroutine时,go将它视为一个独立的工作单元,这个工作单元会被调度到可用的逻辑处理器上执行
go语言运行时调度器: 能管理被创建的所有goroutine并为其分配执行时间,
调度器在操作系统之上,将操作系统的线程和语言运行时的逻辑处理器绑定,并在逻辑处理器上运行goroutine
go语言的并发同步模型: 叫通信顺序进程(communicating Sequential Processes)CSP的范性,CSP是一种消息传递模型,通过在goroutine之间传递数据来传递消息,而不是对数据进行加锁来实现同步访问.
用于在goroutine之间同步和传递数据的关键数据类型叫做通道(channel)
并行: 是让不同的代码片段同时在不同的物理处理器上执行,关键是同时做很多事情
并发: 是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情
下图是一个逻辑处理器上并发运行goroutine和两个逻辑处理器上并行运行两个并发的goroutine
进程: 包含了应用程序运行需要用到和维护的各种资源的容器,这些资源包括但不限于内存地址空间,文件和设备的句柄以及线程
线程:一个执行空间,这个空间被操作系统调度来运行函数中所写的代码
每个进程至少包含一个线程
操作系统会在物理处理器上调度线程来运行
go语言运行时会在逻辑处理器上调度goroutine来运行
每个逻辑处理器都分别绑定在单个操作系统线程上
如果创建一个goroutine并准备运行:
这个goroutine就会被放在调度器的全局运行队列中;
调度器就将这些队列中的goroutine分配一个逻辑处理器,并放在这个逻辑处理对应的本地运行队列中;
本地运行队列中goroutine一直等待自己被分配到逻辑处理器上执行
正在运行的goroutine执行一个阻塞的系统调用:
线程和goroutine会从逻辑处理器上分离;
该线程继续阻塞等待系统调用的返回;
这个逻辑处理器就失去了运行的线程,调度器会新建线程,并将其绑定到该逻辑处理器上;
调度器会从本地运行队列中选择另外的goroutine来运行;
一旦被阻塞的系统调用执行完成并返回,对应的goroutine会放回到本地运行队列,之前的线程会保存好以遍之后使用
如果一个goroutine需要做一个网络I/O调用:
goroutine会和逻辑处理器分离,并移到集成了网络轮询器的运行时;
一旦该轮询器指示某个网络读或者写操作已经就绪,对应的gorouotine会重新分配搭配到逻辑处理器上来完成操作.
调度器对可以运行的逻辑处理器没有限制
语言运行时默认限制每个程序最多创建10000个线程,此限制可以通过调用runtime/debug包中的SetMaxThread方法来更改,如果程序试图使用更多的线程就会崩溃
创建两个goroutine,以并发的形式分别显示大写和小写的英文字母
func main(){
runtime.GOMAXPROCS(1) //分配一个逻辑处理器给调度器使用
var wg sync.WaitGroup
wg.Add(2). //计数器加2,表示要等待两个goroutine
fmt.println("Start Goroutine")
go func(){
defer wg.Done()
for i:=0;i<3;i++{
for char:='a';char<'a'+26;char++{
fmt.printf("%c",char)
}
}
}()
go func(){
defer wg.Done() //通知main函数工作已完成
for i:=0;i<3;i++{
for char:='A';char<'A'+26;char++{
fmt.printf("%c",char)
}
}
}()
fmt.println("Waiting To Finish")
wg.wait()
}
WaitGroup:是一个计数信号量,可以用来记录并维护运行的goroutine.
如果waitgroup大于0,wait方法就会阻塞
defer:会修改函数调用时机,在正在执行的函数返回时才真正调用defer声明的函数
只有在有多个逻辑处理器,且可以同时让每个goroutine运行在一个可用的物理处理器上时,goroutine才会并行运行.
上一篇: 博客网站的经营——1年发展规划方案
下一篇: LayaAir学习笔记01(TS)