并发-Goroutine(协程)

Go学习笔记

  • 轻量级"线程"

    线程(Thread):有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源

    线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程的切换一般也由操作系统调度

    协程(Coroutine):又称微线程与子例程(或者称为函数)一样,协程也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛

    Coroutine

    和线程类似,共享堆,不共享栈,协程的切换一般由程序员在代码中显式控制。它避免了上下文切换的额外耗费,兼顾了多线程的优点,简化了高并发程序的复杂

    Goroutine和其他语言的协程(Coroutine)在使用方式上类似,但从字面意义上来看不同(一个是Goroutine,一个是Coroutine),再就是协程是一种协作任务控制机制,在最简单的意义上,协程不是并发的,而Go语言层面支持并发(Goroutine)。因此Goroutine可以理解为一种Go语言的协程。同时它可以运行在一个或多个线程上

    Goroutine与创建线程相比,创建成本和开销都很小,每个goroutine的堆栈只有几kb,并且堆栈可根据程序的需要增长和缩小(线程的堆栈需指明和固定),所以go程序从语言层面支持了高并发

    Goroutine是Go中最基本的执行单元。事实上每一个Go程序至少有一个Goroutine:主Goroutine

    其它语言支持

    • C++:Boost.Coroutine
    • Java:不支持(有第三方库)
    • Python:使用yield关键字实现协程,3.5加入async def对协程原生支持
  • 非抢占式多任务处理,由协程主动交出控制权

  • 编译器/解释器/虚拟机层面的多任务

  • 多个协程可能在一个或多个线程上运行

简单示例

import (
	"fmt"
	"time"
	"runtime"
)

func main() {
	var a [10]int
	for j := 0; j < 10 ; j++ {
		go func(j int) {
			for{
				a[j]++
				runtime.Gosched() //程序主动交出控制权
			}
		}(j)
	}
	time.Sleep(time.Microsecond)
	fmt.Println(a)
}

可以使用如下命令检测数据访问的冲突

go run -race goroutine.go

调度器

Go调度器

Goroutine的定义

  • 任何函数只需要加上go就能通知调度器运行

  • 不需要在定义时区分是否是异步函数

  • 调度器在合适的点进行切换

    IO,Select

    Channel

    等待锁

    函数调用(非所有)

    runtime.Gosched()

    上述只是切换参考点,不能保证切换,不能不保证其他地方不切换

  • 使用-race检测数据访问冲突

Go 

See also