golang ----------- goroutine(协程)

一、协程

1、协程:Coroutine,轻量级线程

2、协程非抢占式多任务处理,由协程主动交出控制权

     在协程中,什么时候交出控制权,什么时候不交出控制权,是由协程内部主动决定的。线程是抢占式多任务处理(执行一半,操作系统把它断掉,去执行别的),要处理最坏的情况,人家做到一半,要存很多东西,上下文等要存很多东西,会有很多资源消耗。因为协程是非抢占式,所以能够做到轻量级。只要处理切换的几个点就可以了,切换的点都需要我们显示的写出来,对资源的消耗会更少一点。

3、协程是编译器/解释器/虚拟机层面的多任务。

    协程不是操作系统层面上的多任务,(操作系统层面上的多任务是线程,java多线程是虚拟机层面上的),编译器会把go func解释成一个协程,在运行时,由调度器调度协程

4、多个协程可能在一个或多个线程上运行,这个是由调度器控制的

5、子程序(过程,函数)是协程的一个特例

     普通函数:main,function,在一个线程里,单向沟通

     协程:main ,function,可能在一个线程里,可以双向沟通

二、goroutine

1、任何函数只需要加上go就能送给调度器运行

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

3、调度器会在合适的点进行切换,这些点我们不能完全的进行控制,这也是goroutine和传统意义上的协程(切换的点都需要我们显示的写出来)上不太一样的地方。但是又和线程之间的切换不一样,它是有固定的点的(如下三介绍)

4、使用go run -race xxx.go来检测数据访问冲突

5、goroutine是非抢占式的,它和抢占式的有点像,但运行机制是非抢占式的

6、goroutine在哪个线程里也是由调度器控制,可以使用top,查看开了几个线程

golang ----------- goroutine(协程)_第1张图片

三、goroutine切换点

1、I/O,select

2、channel

3、等待锁

4、函数调用(是一个切换的机会,是否会切换由调度器决定)

5、runtime.Gosched()

四、简单例子

例1:协程不交出控制权,一直在一个goroutine里死循环

func main() {
	var a [10]int
	for i := 0; i < 10; i++ {
		go func(i int) {
			for {//会一直在这死循环,这个goroutine会一直不交出控制权,因为控制权一直掌握在自己手里。使用runtime.Gosched()可以交出控制权
				a[i]++ 
			}
		}(i)
	}
	time.Sleep(time.Minute)
	fmt.Println(a)
}

例2::下面这个例子goroutine可以交出控制权

func main() {
	var a [10]int
	for i := 0; i < 10; i++ {
		go func(i int) {
			for {
				fmt.Printf("Hello from goroutine %d\n", i)   //io操作会进行切换,会有等待过程,所以可以让其他协程运行
			}
		}(i)
	}
	time.Sleep(time.Minute)
	fmt.Println(a)
}

例3:数据访问冲突

func main() {
	var a [10]int
	for i := 0; i < 10; i++ {
		go func() {     //数据访问冲突,注意func()里面没i
			for {
				a[i]++ //这里的i会变成10(index out of range)数据访问冲突
			}
		}()
	}
	time.Sleep(time.Minute)
	fmt.Println(a)    //当在读a的时候,上面有goroutine在往a里面写,这也会造成(race condition)数据访问冲突
}

你可能感兴趣的:(golang ----------- goroutine(协程))