协程VS线程:
协程是虚拟机层面的多任务,非抢占式,由协程自己主动交出控制权;而线程是由操作系统调度,夺走控制权;
多个协程可以运行在一个线程,轮流来,主动交权。
主协程退出,pcb销毁,其他协程讲不复存在,只要保证主协程不退出就可以了。
协程切换的时间点:io操作的时候会交出控制权;
如何主动交出控制权:runtime.Gosched(): 主动出让当前go程cpu使用权 一次。(进入就绪态) 提高其他go程获取cpu 的概率。
ps:
GOMAXPROCS: 设置当前参与计算的逻辑cpu个数。 返回上次成功设置的个数。如果第一次设置,返回计算机默认逻辑cpu数。
Numcpu() : 返回计算机默认逻辑cpu数。
GC(): 手动调用垃圾回收器。 回收垃圾内存。
【重点】Goexit(): 结束当前 go程。并且执行成功注册的 defer 指令。
不能在 主 go程 中使用 Goexit() 结束主go程。 —— 异常:fatal error: no goroutines (main called runtime.Goexit) - deadlock!
【重点】比对:
return:返回当前 函数 调用。 return 0(正常) / 非0(1、-1异常)
Goexit():结束当前 go程。
os.Exit(int): 结束 当前 进程。不会指定 defer 注册成功的执行。
首先要了解几个概念:
内核态和用户态
进程:进程可以看作一个包含了应用程序在运行中需要用到和维护的各种资源的容器。
并发和并行:并发是指 goroutine 运行的时候是相互独立的。
普通进程调度:
操作系统将线程调度到某个cpu上运行,这个cpu并不一定是进程所在的cpu。不同操作系统使用
的线程调度算法一般都不一样,但是这种不同会被操作系统屏蔽,并不会展示给程序员。
待续:普通进程调度的资源开销:内核切换进程时候。。。
go程调度:
go自己实现了线程调度,而不是交给操作系统去调度。切换线程的时候:比较节省时间。。。待续
抓住几个概念:go程,go的调度器,逻辑处理器,物理处理器。
调度器创建逻辑cpu(个数可以手动设置),go程在逻辑cpu上运行,而逻辑cpu实际运行在系统线程上(调度器绑定系统线程和逻辑cpu,必要时解绑),系统线程运行在物理cpu上,系统线程也是由go调度器创造的。
go程在逻辑cpu上执行,而逻辑cpu具有独立的系统线程和运行队列(即go程)。
调度器会创建多个逻辑处理器,即逻辑cpu。
当前线程上运行的go程因为系统调用阻塞时,go的调度器,就会把当前线程从逻辑cpu上解除绑定,创造一个新的线程,绑定到这个逻辑cpu上,并且从本地运行队列里头选择一个go程来运行。
PS:包 runtime 提供了修改 Go 语言运行时配置参数的能力。调用 GOMAXPROCS 函数就为每个可用的物理cpu创建一个逻辑处理器(需要强调的是,使用多个逻辑cpu并不意味着性能更好。在修改任何语言运行时配置参数的时候,都需要配合基准测试来评估程序的运行效果)。
PS:如果一个 goroutine 需要做一个网络 I/O 调用,流程上会有些不一样。在这种情况下,goroutine会和逻辑cpu分离,并移到集成了网络轮询器的运行时。一旦该轮询器指示某个网络读或者写操作已经就绪,对应的 goroutine 就会重新分配到逻辑cpu上来完成操作。调度器对可以创建的逻辑cpu的数量没有限制,但语言运行时默认限制每个程序最多创建 10 000 个线程。这个限制值可以通过调用 runtime/debug 包的 SetMaxThreads 方法来更改。如果程序试图使用更多的线程,就会崩溃。