goroutine

goroutine

func main() {
	for i := 0; i < 1000; i++ {
		go func(i int) {
			for {
				fmt.Printf("Hello from "+"goroutine %d\n", i)
			}
		}(i)
	}
	time.Sleep(time.Millisecond)
}

goroutine其实是一种协程coroutine。
协程是轻量级的“线程”,线程是由OS进行调度的,因此在任何时候都可以被抢占的,而协程是非抢占式多任务处理,由协程主动交出控制权。编译器/解释器/虚拟机层面的多任务,不是操作系统层面的多任务,go语言有一个调度器来调度协程。多个协程可以在一个或者多个线程上运行。

func main() {
	//main也是一个goroutine
	var a [10]int
	for i := 0; i < 10; i++ {
		go func(i int) {
			for {
				a[i]++ //在这个goroutine里面没有机会交出控制权
			}
		}(i)
	}
	time.Sleep(time.Millisecond) //因为没人交出控制权,因次这句也执行不了
}

如何交出控制权呢?
I/O操作会交出控制权,还可以手动交出控制权runtime.Gosched()。

//此代码会出现 index out of range的错误 可以使用go run -race 源文件名  进行查错
//因为这里形成了闭包,匿名函数中的a[i]会引用自由变量i,当i=10,跳出循环的时候,调度到a[i]++的goroutine时就会出现index out of range,因此需要将变量作为参数传入。
func main() {
	var a [10]int
	for i := 0; i < 10; i++ {
		go func() { //出现race condition条件竞争
			for {
				a[i]++
				runtime.Gosched()
			}
		}()
	}
	time.Sleep(time.Millisecond)
	fmt.Println(a)
}
//正确代码
func main() {
	//有可能会出现,main在fmt.Println(a),有一个goroutine在a[i]++。出现race condition。需要使用channel来解决。
	var a [10]int
	for i := 0; i < 10; i++ {
		go func(i int) {
			for {
				a[i]++
				runtime.Gosched()
			}
		}(i)
	}
	time.Sleep(time.Millisecond)
	fmt.Println(a)
}

子程序是协程的一个特例。
goroutine_第1张图片
多个协程可能映射到同一个线程。
goroutine_第2张图片
任何函数只需加上go就能送给调度器运行。
不需要在定义时区分是否是异步函数。
调度器在合适的点进行切换。
使用-race来检测数据访问的冲突。

goroutine可能的切换点

  • I/O, select
  • channel
  • 等待锁
  • 函数调用(有时)
  • runtime.Gosched()

以上只是参考,不能保证切换,不能保证在其他地方不切换。

你可能感兴趣的:(go)