Golang教程:goroutine协程
在上一篇中,我们讨论了并发,以及并发和并行的区别。在这篇教程中我们将讨论在Go中如何通过Go协程实现并发。
什么是协程
Go协程(Goroutine)是与其他函数或方法同时运行的函数或方法。可以认为Go协程是轻量级的线程。与创建线程相比,创建Go协程的成本很小。因此在Go中同时运行上千个协程是很常见的。
Go协程对比线程的优点
- 与线程相比,Go协程的开销非常小。Go协程的堆栈大小只有几kb,它可以根据应用程序的需要而增长和缩小,而线程必须指定堆栈的大小,并且堆栈的大小是固定的。
- Go协程被多路复用到较少的OS线程。在一个程序中数千个Go协程可能只运行在一个线程中。如果该线程中的任何一个Go协程阻塞(比如等待用户输入),那么Go会创建一个新的OS线程并将其余的Go协程移动到这个新的OS线程。所有这些操作都是 runtime 来完成的,而我们程序员不必关心这些复杂的细节,只需要利用 Go 提供的简洁的 API 来处理并发就可以了。
- Go 协程之间通过信道(channel)进行通信。信道可以防止多个协程访问共享内存时发生竟险(race condition)。信道可以想象成多个协程之间通信的管道。我们将在下一篇教程中介绍信道。
如何创建一个协程?
在函数或方法调用之前加上关键字 go,这样便开启了一个并发的Go协程。
让我们创建一个协程:
package main import (
"fmt"
) func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
fmt.Println("main function")
}
第11行,go hello() 开启了一个新的协程。现在 hello() 函数将和 main() 函数一起运行。main 函数在单独的协程中运行,这个协程称为主协程。
运行这个程序,你将得到一个惊喜。
程序仅输出了一行文本: main function。我们创建的协程发生了什么?我们需要了解Go协程的两个属性,以了解为什么发生这种情况。
- 当创建一个Go协程时,创建这个Go协程的语句立即返回。与函数不同,程序流程不会等待Go协程结束再继续执行。程序流程在开启Go协程后立即返回并开始执行下一行代码,忽略Go协程的任何返回值。
- 在主协程存在时才能运行其他协程,主协程终止则程序终止,其他协程也将终止。
我想你已经知道了为什么我们的协程为什么没有运行。在11行调用 go hello()后,程序的流程直接调转到下一条语句执行,并没有等待 hello 协程退出,然后打印 main function。接着主协程结束运行,不会再执行任何代码,因此 hello 协程没有得到运行的机会。
让我们修复这个问题:
package main import (
"fmt"
"time"
) func hello() {
fmt.Println("Hello world goroutine")
}
func main() {
go hello()
time.Sleep( * time.Second)
fmt.Println("main function")
}
上面的程序中,第13行,我们调用 time 包的 Sleep 函数来使调用该函数所在的协程休眠。在这里是让主协程休眠1秒钟。现在调用 go hello() 有了足够的时间得以在主协程退出之前执行。该程序首先打印 Hello world goroutine,等待1秒钟之后打印 main function。
在主协程中使用 Sleep 函数等待其他协程结束的方法是不正规的,我们用在这里只是为了说明Go协程是如何工作的。信道可以用于阻塞主协程,直到其他协程执行完毕。我们将在下一篇教程中讨论信道。
开启多个协程
让我们写一个程序开启多个协程来更好的理解协程:
package main import (
"fmt"
"time"
) func numbers() {
for i := ; i <= ; i++ {
time.Sleep( * time.Millisecond)
fmt.Printf("%d ", i)
}
}
func alphabets() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep( * time.Millisecond)
fmt.Printf("%c ", i)
}
}
func main() {
go numbers()
go alphabets()
time.Sleep( * time.Millisecond)
fmt.Println("main terminated")
}
上面的程序在第21和22行开启了两个协程。现在这两个协程同时执行。numbers 协程最初睡眠 250 毫秒,然后打印 1,接着再次睡眠然后打印2,以此类推,直到打印到 5。类似地,alphabets 协程打印从 a 到 e 的字母,每个字母之间相隔 400 毫秒。主协程开启 numbers 和 alphabets 协程,等待 3000 毫秒,最后终止。
程序的输出为:
a b c d e main terminated
下面的图片描述了这个程序是如何工作的

上图中,蓝色的线框表示 numbers 协程,栗色的线框表示 alphabets 协程。绿色的线框表示主协程。黑色的线框合并了上述三个协程,向我们展示了该程序的工作原理。每个框顶部的 0ms,250 ms 的字符串表示以毫秒为单位的时间,在每个框底部的 1,2,3 表示输出。
蓝色的线框告诉我们在 250ms 的时候打印了1,在 500ms 的时候打印了2,以此类推。因此最后一个线框底部的输出:1 a 2 3 b 4 c 5 d e main terminated 也是整个程序的输出。上面的图像是很好理解的,您将能够了解该程序的工作原理。
本文转自:https://blog.csdn.net/u011304970/article/details/75096323?locationNum=3&fps=1
Golang教程:goroutine协程的更多相关文章
- Golang 入门 : goroutine(协程)
在操作系统中,执行体是个抽象的概念.与之对应的实体有进程.线程以及协程(coroutine).协程也叫轻量级的线程,与传统的进程和线程相比,协程的最大特点是 "轻"!可以轻松创建上 ...
- Golang的goroutine协程和channel通道
一:简介 因为并发程序要考虑很多的细节,以保证对共享变量的正确访问,使得并发编程在很多情况下变得很复杂.但是Go语言在开发并发时,是比较简洁的.它通过channel来传递数据.数据竞争这个问题在gol ...
- golang中goroutine协程调度器设计策略
goroutine与线程 /* goroutine与线程1. 可增长的栈os线程一般都有固定的栈内存,通常为2MB,一个goroutine的在其声明周期开始时只有很小的栈(2KB),goroutine ...
- golang中最大协程数的限制(线程)
golang中最大协程数的限制 golang中有最大协程数的限制吗?如果有的话,是通过什么参数控制呢?还是通过每个协程占用的资源计算? 通过channel控制协程数的就忽略吧. 以我的理解,计算机资源 ...
- go语言之进阶篇创建goroutine协程
1.goroutine是什么 goroutine是Go并行设计的核心.goroutine说到底其实就是协程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现 ...
- golang的多协程实践
go语言以优异的并发特性而闻名,刚好手上有个小项目比较适合. 项目背景: 公司播控平台的数据存储包括MySQL和ElasticSearch(ES)两个部分,编辑.运营的数据首先保存在MySQL中,为了 ...
- Go goroutine (协程)
在Go语言中goroutine是一个协程,但是跟Python里面的协程有很大的不同: 在任何函数前只需要加上go关键字就可以定义为协程; 不需要在定义时区分是否是异步函数 VS async def ...
- go 学习 (五):goroutine 协程
一.goroutine 基础 定义 使用者分配足够多的任务,系统能自动帮助使用者把任务分配到 CPU 上,让这些任务尽量并发运作,此机制在Go中称作 goroutine goroutine 是 Go语 ...
- golang:Channel协程间通信
channel是Go语言中的一个核心数据类型,channel是一个数据类型,主要用来解决协程的同步问题以及协程之间数据共享(数据传递)的问题.在并发核心单元通过它就可以发送或者接收数据进行通讯,这在一 ...
随机推荐
- Java集合框架(Collection Framework)学习之 Collection与Map概貌
写过Java的人都知道Java集合类,也用过Java集合类.Java集合类位于 java.util 这个包下,就像它的包名暗示的那样,Java集合类就是一套工具.它就像工匠的工具箱一样,它能给使用它的 ...
- List分组迭代器
说明: 针对长度较大的List对象,可以分组批量进行处理, 如:长度为1000的List对象,可分为10组,每组100条,对数据进行业务逻辑处理... Source /**************** ...
- 高德地图之c#后台获取一个或多个起点到单个终点的直线距离
首先我们需要一个控制台添加一个新Key(可使用服务选择Web服务,测试的时候IP白名单先不填); 直线距离是通过后台get方式请求API服务地址http://restapi.amap.com/v3/d ...
- docker : RabbitMQ ElasticSearch
docker 运行RabbitMQ容器 docker run -d -p 5672:5672 -p 15672:15672 --name 命名 CONTAINER ID 放出5672 / 156 ...
- OI图论 简单学习笔记
网络流另开了一个专题,所以在这里就不详细叙述了. 图 一般表示为\(G=(V,E)\),V表示点集,E表示边集 定义图G为简单图,当且仅当图G没有重边和自环. 对于图G=(V,E)和图G2=(V2,E ...
- ERWin7.2在Windows 8.1中生成数据库遇到的问题!
我在Windows 8.1中使用ERWin7.2,想象以前在windows 7 或者XP里那样先预览一下要生成的SQL语句,结果我发现居然没有“preview”按钮了,自然也就无法看到要生成的SQL语 ...
- python3 的zip()函数
重点 https://blog.csdn.net/qq826364410/article/details/78259796 为啥会出现几个两个空列表????
- S:List
描述 写一个程序完成以下命令:new id ——新建一个指定编号为id的序列(id<10000)add id num——向编号为id的序列加入整数nummerge id1 id2——合并序列id ...
- 洛谷P5273 【模板】多项式幂函数 (加强版)
题面 传送门 题解 这里最麻烦的问题就是它不保证\(A_0=1\) 如果\(A_0>1\),那么直接整个多项式乘上个\(A_0\)的逆元,最后输出答案的时候再把答案乘上\({A_0}^m\) 如 ...
- c调用 lua 栈操作
转自https://www.cnblogs.com/ringofthec/archive/2010/10/22/lua.html 打算记录一些lua_api, 可能会觉得lua文档中已经说的很清楚了, ...