我们每运行的一个程序都会创建一个进程,每个进程都有一个初始线程,而后初始线程可以创建更多线程,每个线程互相独立地运行。线程因为其轻量和易用性在并发编程中被大量的使用。而 goroutine 就是基于线程的。线程的实现模型主要有3种:用户级线程模型、内核级线程模型和两级线程模型(或者叫做混合型线程模型)。他们之间的区别就是用户线程和系统最小调度单元内核调度实体(KSE,Kernal Scheduling Entity)的对应关系不同。
用户线程与内核线程KSE是多对一的映射模型,多个用户线程一般都是从属于单个进程,并且用户线程的调度都是用户程序的线程库完成的,不需要操作系统调度。一个进程所创建的所有线程都和同一个KSE绑定,操作系统调度只知道进程对线程无感知,这种模型实现的线程调度是用户层实现的不需要操作系统内核调度所以是轻量级的。但是这种模型最根本缺点就是并不能实现真正意义上的并发,因为单个进程的所有线程都是绑定在同一个KSE,各个用户线程交替执行,如果某一个用户线程阻塞了,那么整个进程就会阻塞。
用户线程与内核线程KSE是一对一(1:1)的映射模型,也就是每一个用户线程绑定一个真实的内核线程,那么线程调度就完全依赖于操作系统的内核调度,java语言等就是这种模式,这种模式虽然实现了真正的并行但是线程调度代价很大,严重影响系统性能。
两级线程模型综合了上面两种模式优点,在此模型中用户线程和内核线程KSE是多对多的关系,与内核级线程模型不同的是两级线程模型中的进程可以和多个KSE绑定,而进程中的线程并不会和某一个KSE绑定,而会动态调整,当进程中的某一个线程阻塞了某一个KSE,那么进程中其余的线程会去绑定到其他的KSE。所以两级线程模型即不像用户线程模型完全靠用户调度也不像内核级线程模型完全依赖系统调度,而是一个中间态的模型。而 goroutine 就是调度器就是采用这种模型。
golang调度系统中有3个比较重要的概念那就是G、P、M:

根据图中2个概念需要说明:
当我们通过 go 关键字创建一个新的 goroutine 的时候,他会优先被放入到P的本地运行队列,如果P运行队列已满,就会放入到Global 队列中。每个M会绑定到一个 KSE ,并随机选择一个 P ,然后 M 会启动一个 OS 线程循环从绑定的P里获得 goroutine 并执行。直到P中G执行完毕,然后P会尝试从 Global 队列里获得G来执行,如果 Global 队列为空那么P会随机挑选另外一个P,从它的队列里拿来一般的G到自己的队列中执行。
P的数量默认是CPU的数量或者 runtime.GOMAXPROCS() 来决定,在确定了P的数量后,go 运行时系统会创建相应数量的P。当P中本地运行队列不为空的时候,P会去绑定一个M,如果没有多余的M的话,那么系统回去创建一个M,go 系统会设置M最大数量限制,最大为10000。M和P没有绝对的关系,M会随机选择一个P绑定,当M阻塞的时候而P的本地运行队列不为空那么P就会去创建或者切换到一个新的M,所以即使P的默认数量是1也会创建出多个M。
非著名程序员,全栈开发工程师,长期专注系统开发与架构设计。
功能待开通!
gorm 简介 gorm 是 go 语言中实现的比较好的 ORM 包,且是国人开发的。项目地址 事故描述 Scan 是 gorm 提供的一个把数据库结果读取到 struct 的函数。定义如下: // Scan scan value to a struct func (s *DB) Scan(dest interface{}) *DB { return s.NewScope(s.Value).Set("gorm:query_destination", dest).callCallbacks(s.parent.callbacks.queries).db } 今天同事小张写代码的时候写了一个
三个工具介绍 go fmt是用来规范go文件格式,比如格式化单个文件 go fmt xxx.go goimports 用来检查导入包,导入依赖包,删除不依赖的包 gometalinter 集成go语言几乎所有检测工具,静态分析代码,包含功能如下 go vet -工具可以帮我们静态分析我们的源码存在的各种问题,例如多余的代码,提前return的逻辑,struct的tag是否符合标准等。 go tool vet --shadow -用来检查作用域里面设置的局部变量名和全局变量名设置一样导致全局变量设置无效的问题 gotype -类型检测用来检测传递过来的变量和预期变量类型一致 gotype -x
Go 语言的可移植性 Java 平台可移植性是众所都知的,Java 的可移植性依赖于其虚拟机 JVM,Java 实现了对不同平台的 JVM 的支持,那么一份 Java 代码就可以在各个平台上运行。而 Go 语言的可移植性也是依赖于其 runtime,runtime 去对接操作系统层,用户代码在 runtime 中运行,用户代码就不用去关心平台问题。 查看 Go 支持的OS和平台: > $ go version go version go1.11 darwin/amd64 liushuai@liushuaideMacBook-Pro ~/Documents/goProject/src
临时忽略掉struct中空字段 type User struct { Email string `json:"email"` Password string `json:"password"` } 当我们把用户信息返回给前端的时候显然需要忽略调Password 字段,则可以这样做: json.Marshal(struct{ *User Password bool `json:"password,omitempty"` }{ User:user, }) 临时添加额外字段 type User struct { Email string `json:"
golang中字符串格式化输出 package main import ( "fmt" "os" ) type point struct { x, y int } func main() { // Go提供了几种打印格式,用来格式化一般的Go值,例如 // 下面的%v打印了一个point结构体的对象的值 p := point{1, 2} fmt.Printf("%v\n", p) // 如果所格式化的值是一个结构体对象,那么`%+v`的格式化输出 // 将包括结构体的成员名称和值 fmt.Printf("%