Go---并发

291 阅读2分钟

goroutine

go中使用goroutine来实现并发,与其它语言中的thread有所不同,goroutine更轻量,一个thread可以对应多个goroutine,所以在某些场景下,goroutine的并发效果要远好于thread,这也是go语言的一大优势。

go关键字

go中开启一个goroutine的方式就是在要实现并发的函数前面加一个go关键字,如

import (
	"fmt"
	"sync"
)

var (
	x  = 0
	wg sync.WaitGroup
)

// x自增100次
func add() {
	for i := 0; i < 100; i++ {
		x++
		fmt.Println(x)
		wg.Done()
	}
}

func main() {
	wg.Add(200)
	for i := 0; i < 2; i++ {
		go add()
	}
	wg.Wait()
}

sync.WaitGroup

上面的例子中,如果没有wg.Add()、wg.Done()、wg.wait()这些,也许控制台上不会有任何输出。因为goroutine是异步的,main函数不会等add函数结束后才结束,main函数结束后,add函数自然就结束了,但main函数结束过快就会导致add函数中的输出还没打印出来,程序就结束了,控制台上就看不到输出结果,所以我们需要让main函数推迟结束,但使用time.Sleep()又过于死板,即使add函数提前结束了,main也依然会等待指定时间后才会结束。这就会造成资源的浪费。

  • wg.Add():加几次任务
  • wg.Done():减一次任务
  • wg.Wait():当任务数为0时,就向下执行,否则就等待 以上是sync.WaitGroup的三个方法,这三个方法保证了goroutine执行结束后,main函数才能结束,而且是goroutine一结束,main函数就结束了,避免了无谓的等待,比较灵活。

GMP模型

G

G代表goroutine对象,它包括栈、指令指针以及对于调用goroutines很重要的其它信息,比如阻塞它的任何channel等。每次执行go都会创建一个G对象。

M

M(Machine),抽象化代表内核线程,记录内核线程栈信息,当goroutine调度到线程时,使用该goroutine自己的栈信息。M需要与P进行绑定。

P

P(Processor)是一个抽象的概念,并不是真正的物理CPU。所以当P有任务时需要创建或者唤醒一个系统线程来执行它队列里的任务。所以P/M需要进行绑定,构成一个执行单元。

P决定了同时可以并发任务的数量,可通过GOMAXPROCS限制同时执行用户级任务的操作系统线程。可以通过runtime.GOMAXPROCS进行指定。在Go1.5之后GOMAXPROCS被默认设置可用的核数,而之前则默认为1。

end

文章仅做个人学习交流,也许并不完全正确,欢迎指正;