2020-11-30 20:05:56
摘要:Processes and Threads
操作系统会为该应用程序创建一个进程。作为一个应用程序,它像一个为所有资源而运行的容器。这些资源包括内存地址空间、文件句柄、设备和线程。
线程是操作系统调度的一种执行路径,用于在处理器执行我们在函数中编写的代码。一个进程从一个线程开始,即主线程,当该线程终止时,进程终止。这是因为主线程是应用程序的原点。然后,主线程可以依次启动更多的线程,而这些线程可以启动更多的线程。
无论线程属于哪个进程,操作系统都会安排线程在可用处理器上运行。每个操作系统都有自己的算法来做出这些决定。
Goroutines and Parallelism
Go 语言层面支持的 go 关键字,可以快速的让一个函数创建为 goroutine,我们可以认为 main 函数就是作为 goroutine 执行的。操作系统调度线程在可用处理器上运行,Go运行时调度 goroutines 在绑定到单个操作系统线程的逻辑处理器中运行(P)。即使使用这个单一的逻辑处理器和操作系统线程,也可以调度数十万 goroutine 以惊人的效率和性能并发运行。
并发不是并行。并行是指两个或多个线程同时在不同的处理器执行代码。如果将运行时配置为使用多个逻辑处理器,则调度程序将在这些逻辑处理器之间分配 goroutine,这将导致 goroutine 在不同的操作系统线程上运行。但是,要获得真正的并行性,您需要在具有多个物理处理器的计算机上运行程序。否则,goroutines 将针对单个物理处理器并发运行,即使 Go 运行时使用多个逻辑处理器。
Keep yourself busy or do the work yourself
比如我们想监听一个端口,但并不知道它什么时候返回,我们可能会使用一个select来永远阻塞,如下代码:
func main() {
http.HandleFunc(/, func(w http.ResponseWriter, r *http.Request)) {
fmt.FprintIn(w, Hello, GrpherCon SG)
}
go func() {
if(err := http.listenAndServe(:8080, nil); err != nil) {
log.Fatal(err)
}
}()
sele……
阅读全文
2020-11-28 20:24:53
摘要:Error
error类型是go语言的一种内置类型,使用的时候不用特定去import,他本质上是一个接口
//http://golang.org/pkg/builtin/#error
type error interface{
Error() string //Error()是每一个订制的error对象需要填充的错误消息,可以理解成是一个字段Error
}
我们经常使用 errors.New() 来返回一个 error 对象。
//http://golang.org/src/pkg/errors/errors.go
type errorString struct {
s String
}
func (e *errorString) Error() string {
return e.s
}
基础库中有大量自定义的error,如bufio
//http://golang.org/src/pkg/bufio/bufio.go
var {
ErrInvalidUnreadByte = errors.New(bufio: invalid use of UnreadByte)
ErrInvalidUnreadRune = errors.New(bufio: invalid use of UnreadRune)
ErrBufferFull = errors.New(bufio: Buffer full)
ErrNegativeCount = errors.New(bufio: negative count)
}
errors.New()返回的是内部errorString对象的指针
//http://golang.org/src/pkg/errors/errors.go
//New returns an error that formats as the given text
func New(text string) error {
return errorString{text}
}
Error vs Exception
各个语言的演进历史:
C:单返回值,一般通过传递指针作为入参,返回值为 int 表示成功还是失败。
C++:引入了 exception,但是无法知道被调用方会抛出什么异常
Java:引入了 checked exception……
阅读全文
2020-11-21 11:39:28
摘要:Go语言GC算法主要是基于Mark and Sweep (标记清除)算法,在此基础上进行改进和优化。
1). Mark and Sweep(标记清除)算法主要是以下2个步骤:
标记(Mark): 找出所有不可达对象,然后做上标记
清除(Sweep): 回收标记好的对象
标记清除具体步骤如下:
a). 开始标记,程序暂停
b). 找到所有可达对象,并做上标记
c). 标记完成后开始清除未标记的对象
d). 清除完成
2). 标记清除算法存在以下几个问题
a). STW (stop the world) 标记对象的时候程序需要暂停,导致程序出现卡顿 (最主要的问题)
b). 标记需要扫描整个堆
c). 清除对象会产生堆碎片
STW指的是runtime把所有的协程都冻结了,意味着用户逻辑是暂停的,这样所有的对象都不会被修改,这个时候去扫描是绝对安全的。
3). Tri-color Marking
为了解决标记清除算法带来的问题,Go在标记清除算法基础上提出来Tri-color Marking(三色标记法)算法来优化GC过程,大体流程如下:
a). 最开始所有的对象都是白色的
b). GC开始,扫描所有可达的对象,标记为灰色
c). 从灰色对象中找到其引用对象并标记为灰色,自己标记为黑色
d). 监控对象修改,循环上一步骤,直到没有任何灰色对象
e). GC回收白色对象
f). 最后把所有黑色对象变成白色
4). 三色标记法通过2点来优化STW问题
a). 标记操作和用户逻辑并行:
用户逻辑经常会生成或改变对象引用,那如何保证标记和用户逻辑并行呢?Go为了解决这个问题引入了写屏障机制,在GC的过程中会监控对象的内存修改,并对对象进行重新标记,这个时候用户逻辑也可以执行 (实际上是很短暂的STW,然后对对象重新标记),所以标记操作可以做到一定程度和用户逻辑并行。
b). 清除操作和用户逻辑并行:
三色标记法中最后只剩下的黑白两种对象,黑色对象是程序恢复后接着使用的对象,如果不碰触黑色对象,只清除白色的对象,肯定不会影响程序逻辑,所以清除白色对象和用户逻辑可以并行。
通过允许用户逻辑在标记和清除操作上做到并行处理来缩短STW的时间,提升整体GC的性能。
阅读全文