golang中并发gorutine

Posted Leo Han

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang中并发gorutine相关的知识,希望对你有一定的参考价值。

我们知道golang的一个重要特性就是能够支持极高的并发。而实现这个特性则是golang中的rorutine机制。在说goroutine之前,我们先说明几个概念:进程、线程、协程

进程:程序运行的基本单位,一个运行的程序就是一个进程,进程之间相互隔离,拥有不同的内存空间,无法共享内存数据。
线程:线程也可以说是轻量级的进程,一般一个程序(或者说一个进程)包含若干个线程,线程依赖于进程,一般进程是作为分配资源的基本单位,而把线程作为独立运行和调度基本单位。同一个进程下的不同线程拥有同一个内存空间,可以共享内存数据。可以说,进程是一个容器,里面装载着线程。
协程:协程是比线程更小粒度的,也称微线程。一般进程和线程都是操作系统级别的,由操作系统去实现,而协程是编译器级别的,不被操作系统控制,完全由程序自己去实现、控制,能够避免线程切换带来的开销。协程的开销要比线程小得多。

操作系统调度CPU的最小单位是线程,多个线程交替抢占CPU的时间片。一般情况下,系统硬件的CPU是有限制的,但是线程数量远远大于CPU的数量,为使每个线程都能够抢占到CPU的时间片,系统都会在一定时间通过中断来进行CPU上执行的线程,在切换线程的时候,为了使下次调度到同一个线程执行时程序状态保持正确,在进行线程切换的时候需要保存这个线程执行的状态,也就是俗称的线程上下文。当发生线程上下文切换时,需要从操作系统的用户态切换到内核态,记录被切换的线程的上下文相关信息,同时将要执行的下一个线程的上下文信息加载到CPU寄存器中,同时从内核态切换到用户态。如果切换的线程是不同的进程,那么需要更新额外的状态信息以及内存地址空间信息,导入新的页表。
进程之间的上下文切换最大的问题在于不同进程地址空间不一样,切换地址空间导致缓存失效,所以不同进程的线程切换一般要明显慢于同一进程内的线程切换
golang中实现协程则是通过goruntine实现,具体代码级别则是通过go关键字:

func GoID() int 
	var buf [64]byte
	n := runtime.Stack(buf[:], false)
	idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
	id, err := strconv.Atoi(idField)
	if err != nil 
		panic(fmt.Sprintf("cannot get goroutine id: %v", err))
	
	return id

func GoroutineTest() 
	fmt.Println("this is a goroutine ",GoID())


func main() 
	go GoroutineTest()
	fmt.Println("this is main ",GoID())
	time.Sleep(time.Duration(1000))
	// 打印
	// this is main  1
    // this is a goroutine  6


在这里通过go关键字,采用另外一个协程去调度GoroutineTest方法。

需要注意的是,使用go关键字创建Goroutine的时候,被调用的函数即使有返回值也会被忽略,如果需要返回数据,或者和其他Goroutine共享数据,则必须通过通道channel

Goroutine的调度执行是由golang自己实现的,基于G-P-M模型,其中G代表Goroutine也就是golang的协程;P代表Processor,golang的逻辑处理器;M代表Machine,代表的则是实际的线程。
在任一时刻,一个P可能再起本地包含多个G,但是一个P在任一时刻只能绑定一个M。需要注意的是,一个G并不是定绑定在一个P上的,有多重情况会导致一个P中的G转移到其他的P中。同样,一个P只能对应一个M,但是具体对应哪一个M也不是固定的。

其实,这就有点类似于P相当于是一个队列的概念,将G放入到P中,然后给M调度执行。

golang中创建Goroutine的代价很小,每个Goroutine的堆栈只有几kb大小,能够根据程序的需要进行增长和收缩。
Goroutine是通过抢占式任务处理

下面说几个系统级相关的方法:

// 设置程序可用的最大CPU数量
runtime.GOMAXPROCS(8)

// 出让当前Goroutine的时间片,类似于java中的Thread.yield
runtime.Gosched()

以上是关于golang中并发gorutine的主要内容,如果未能解决你的问题,请参考以下文章

Go语言并发介绍

Golang 并发: goroutine and channel

Golang 并发: goroutine and channel

Golang 并发: goroutine and channel

Golang 并发: goroutine and channel

Go语言之Go语言并发