协程(coroutine)是一种比线程更轻量级的并发执行单元,它的切换开销和内存栈的占用大小都比线程要小。只要内存足够,在一个线程中可有上万个或更多的协程。除了这些优点,对开发人员来说,在网络编程应用中,采用协程后的业务代码比那些采用异步或事件回调方式的代码更好维护。使用协程的业务逻辑代码表面看上去是同步执行的,编写这些代码时思维是连贯的,更符合人类的思维习惯;而采用异步和回调方式后,业务逻辑代码被分割(分拆)在多个回调方法中,开发人员的思维需要切换,是跳跃的,这样显然容易出错,并且一些场景下还要设计事件或会话上下文数据结构,来保存那些需要在多个回调方法间共享的状态数据。
Golang的一大特色就是从语言层面原生支持协程,在函数或者方法前面加 go关键字就可创建一个协程。
协程的执行线程
协程是在线程中执行的,在Golang中使用环境变量GOMAXPROCS来控制协程使用的线程数,它的缺省值是1,在1.5版本中改为cpu核数。也可在代码中调用
runtime.GOMAXPROCS(n int)函数来设置协程使用的线程数,例如
runtime.GOMAXPROCS(runtime.NumCPU()) 将协程使用的线程数设为cpu个数。
一个Golang进程启动后,该进程中的线程除了协程使用的GOMAXPROCS个线程外,通常还有其他额外的线程。当协程中的代码阻塞在一些系统调用中时,会产生或占用额外的线程,这些线程不算在(不包括)协程占用的GOMAXPROCS个线程中。Golang文档中关于GOMAXPROCS的描述如下:
The GOMAXPROCS variable limits the number of operating system threads that can execute user-level Go code simultaneously. There is no limit to the number of threads that can be blocked in system calls on behalf of Go code; those do not count against the GOMAXPROCS limit. This package's GOMAXPROCS function queries and changes the limit.
当协程阻塞在下列系统调用中时不会产生或占用额外的线程,此时阻塞的协程会被交换出去,协程调度器会调度执行其他可运行的协程。
- network input (socket io)
- sleeping
- channel operations
- blocking on primitives in the sync package.
其他的系统调用会产生额外的线程,例如disk io。如果很多协程同时读写磁盘文件会导致出现很多额外的线程。也就是Golang目前对network socket io会多路复用线程,对disk io不会复用线程。不过github上有个文件io包已经解决了该问题,使用该包读写文件不会出现过多线程。该包见https://github.com/npat-efault/poller,只适用于linux系统。
协程调度器
Golang 的调度器基本上是协作式,而不是抢占式,但也不是完全的协作式调度,在系统调用的函数入口处会有抢占。
例如对如下代码:
package main
import "fmt"
import "runtime"
import "time"
func cpuIntensive(p *int) {
for {
*p++
}
}
func other() {
...
}
func main() {
runtime.GOMAXPROCS(1)
x := 0
go cpuIntensive(&x)
time.Sleep(2* time.Millisecond)
go other()
time.Sleep(100000 * time.Second)
}
上面的代码中,cpuIntensive函数中有个无限循环,当执行go cpuIntensive(&x) 后,它会一直执行不被抢占,导致后面的other协程和main协程饿死,不会执行其中的代码,除非在无限循环中调用runtime.Gosched()让出cpu或加入系统调用函数。
上面的loop循环问题有望在Golang1.6中得到解决,也就是不再需要开发人员显式在代码中加入runtime.Gosched()。
另外当main函数退出时,Golang进程会直接退出,不会等待进程中未完成的协程。
参考文档:
- https://github.com/golang/go/issues/11462
- https://groups.google.com/forum/#!topic/golang-nuts/j51G7ieoKh4
相关推荐
`Go-一个基于golang实现的协程安全的mysqlbuilder`项目旨在提供一个高效、安全且线程安全的MySQL查询构造器,尤其适用于并发环境中。该项目可能包含源码、示例和文档,帮助开发者更好地理解和使用这个库。 首先,...
第3章Go的协程rar 第4章示例环境搭建ram 第5章Go批里生成日志ar 第6章统计系统框架构成.rar 第7章统计统之口志费.rar 第8章统计系统之批星解析a 第9章统计系统之统计逻辑,rar 第10统计系统之存储器rar 第11章据可视...
在`Submit`方法中,我们将任务函数和参数一起包装为匿名函数并发送到任务队列,这样在工作协程中执行时,参数会正确传递给任务函数。 使用这个通用协程池的示例可能如下: ```go pool := NewPool(10) defer pool....
Golang协程调度器原理与GMP设计思想 1. Golang协程调度器 Golang的协程调度器(scheduler)负责管理Go协程(goroutines)的执行。协程是Go语言中的轻量级线程,调度器的任务是高效地将这些协程分配到操作系统线程上...
基于Golang协程实现流量统计系统 最近刚出来的 我对这方面不感兴趣 只是拿出来分享 顺便挣点积分 谢谢大家 如果没有积分可以加群45.8.3(防删)74.310 群文件获取
百度网盘资源,亲测可用,只是为了点积分,若侵权请告知立删
上述代码展示了一个简单的Golang协程池实现。首先,定义一个`SimplePool`结构体,包含一个`sync.WaitGroup`用于同步等待所有工作协程完成,以及一个`work`通道作为任务队列。`NewSimplePoll`函数初始化协程池,根据...
Golang协程调度器的深入分析 Go语言的协程机制,是本文讨论的主题,它深入探讨了线程、调度器和协程之间的关系。首先,文章介绍了Go语言采用的计算模型,它是基于C.A.R. Hoare在1978年提出的通信顺序进程的概念。Go...
1. **协程池**:协程池是一组预先创建的协程,任务被分配到这些协程中执行。这种设计可以避免频繁创建和销毁协程带来的性能损失。 2. **动态调整**:`dynamic-goroutine-pool` 库的关键特性是能够根据系统负载或...
基于Golang协程实现流量统计系统系列课程.txt
1. **定义任务接口**:首先,我们需要定义一个任务接口,它通常包含一个`Run`方法,该方法将在协程中执行。 ```go type Task interface { Run() } ``` 2. **创建工作队列**:我们可以使用`channel`作为工作队列,...
Golang,通常被称为Go语言,是一种由谷歌开发的编程语言。它以简洁、高效、安全著称,并且非常适合构建大规模的分布式系统和微服务。Go语言的一些主要特点包括: 1. **简洁性**:Go语言的语法非常简单,易于学习。 ...
start -> 2021年3月16日22:08:32use this project在config.json配置文件里根据json对应格式配置好账号密码后...123待完善 :获取分类所有ID时,天天酷跑分类怎样都获取不到细细回忆这个软件 竟没有一丝美好回忆end......
标题中提到的关键知识点是“golang 40行代码实现通用协程池”,这说明文章介绍的内容是如何利用Go语言(golang)在非常少的代码行数(40行)内创建一个通用的协程池来管理并发任务。在理解了这一概念之后,我们可以...
基于Golang协程实现流量统计系统 用到技术:Go基础知识,Go的协程,Nginx收集日志,Redis存储,PHP提供API,Ant Design做可视化输出 慕课网课程,分享百度网盘资源: : 更多Go语言实战课程推荐: Go实战仿百度云盘...
在本文中,我们将深入探讨基于epoll协程池的Golang网络库的实现与应用,主要涉及Golang的网络编程和epoll事件模型。epoll是Linux系统提供的一种高效I/O事件通知机制,而协程池则是一种优化并发处理的技术。这种结合...
golang版本的RabbitMQ消息订阅的封装,可以多个生产者,多个消息订阅者,每个队列可以创建多个协程处理; 可能有些消费者需求需要分布式部署,那需要指定队列名,并且需要设置队列持久化,以及排他性QueueDeclare....
平台采用微服务架构,将核心功能拆分为六个微服务,并使用 Golang 的协程并发模型来提升性能。针对高频访问的首页,平台实现了二级缓存和对象池优化,并提出了“异步下订单,本地扣库存”方案来提高下订单功能的执行...
Golang 提供了database/sql包用于对SQL数据库的访问, 作为操作数据库的入口对象sql.DB, 主要为我们提供了两个重要的功能: •sql.DB 通过数据库驱动为我们提供管理底层数据库连接的打开和关闭操作. •sql.DB 为我们...