Go学习:协程和调度器

编程入门 行业动态 更新时间:2024-10-16 18:41:00

Go学习:协程和调度器

Go学习:协程和调度器

协程

go func() {}()

go语言对并发编程有一个原生的支持,这个再通用型语言里面是不常见的.

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


控制台什么都没有打印直接退出了;

  • 匿名函数加上go;就不是说再main里面调用这个函数,而是开一个goroutine并发的执行这个函数
  • 因为我们是并发执行的,main和匿名函数都是并发执行的,程序还来不及打印东西,main就把循环执行完了,然后main就退出了.go语言的程序一旦main退出了,所有的goroutine都会被杀死;那些goroutine来没来得及执行就被干掉了;所以控制台没有打印内容

time.Sleep

package mainimport ("fmt""time"
)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)
}

非抢占式多任务处理

package mainimport ("fmt""time"
)func main() {var arr [10]intfor i := 0; i < 10; i++ {go func(i int) {for {arr[i]++}}(i)}time.Sleep(time.Millisecond)fmt.Println(arr)
}



代码再死循环中一直退不出来,是因为goroutine没有机会交出控制权;交不出控制权就只能死循环下去.

runtime.Gosched

package mainimport ("fmt""runtime""time"
)func main() {var arr [10]intfor i := 0; i < 10; i++ {go func(i int) {for {arr[i]++runtime.Gosched()}}(i)}time.Sleep(time.Millisecond)fmt.Println(arr)
}


runtime.Gosched() 可以手动的交出控制权

go run -race

package mainimport ("fmt""runtime""time"
)func main() {var arr [10]intfor i := 0; i < 10; i++ {go func() {for {arr[i]++runtime.Gosched()}}()}time.Sleep(time.Millisecond)fmt.Println(arr)
}


如果直接引用循环中的i程序就会报错:


我们直接引用循环中的i,goroutine函数内的i就是一个闭包,循环中的i和goroutine函数内的i是一个i,再循环结束的时候i已经等于10,goroutine函数再去引用i时,就会报错

package mainimport ("fmt""runtime""time"
)func main() {var arr [10]intfor i := 0; i < 10; i++ {go func(i int) {for {arr[i]++runtime.Gosched()}}(i)}time.Sleep(time.Millisecond)fmt.Println(arr)
}

总结

  • 轻量级"线程"
  • 非抢占式多任务处理,由协程主动交出控制权
  • 编译器/解释器/虚拟机层面的多任务;不是操作系统层面的多任务
  • 多个协程可能再一个或多个线程上运行

其他语言中的协程

  • C++: Boost.Coroutine

  • Java:不支持;Java不是完全不支持,有一些第三方的JVM会在标准的JVM的基础上加一些对协程的支持

  • python

    • 3.5以前使用yield关键字实现协程
    • 3.5加入了async def对协程的原生支持

调度器

概念


  • 有些协程可能放在一个线程里面,有些协程可能两个放在一个线程里面,有些协程可能多个协程放在一个线程里面;到底怎么存放是由调度器来控制的
package mainimport ("fmt""time"
)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.Second)
}


开了1000个协程,但其实只在12个线程上面运行!

协程的定义

  • 任何函数只需加上go就能送给调度器运行
  • 不需要再定义时区分是否异步函数
  • 调度器再合适点进行切换
  • 使用-race来检测数据访问的冲突

协程可能的切换点

  • I/O,select
  • channel
  • 等待锁
  • 函数调用(有时)
  • runtime.Gosched()
  • 以上几点只是参考,不能保证切换,不能保证再其他的地方不切换

更多推荐

Go学习:协程和调度器

本文发布于:2023-07-28 19:27:22,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1287147.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!