《快学 Go 语言》第 11 课 —— 千军万马跑协程

Selenium+Python自动化测试实战项目 维度unittestpytest学习成本低(标准库)中等语法简洁性❌ 需继承类✅ 函数式风格参数化支持需ddt扩展✅ 原生支持并发执行❌ 不支持✅xdist支持报告能力文本输出HTML/Allure 多种选择社区活跃度稳定但缓慢极其活跃✅结论:新项目强烈建议使用 pytest。不过好消息是,pytest可以直接运行unittest写的测试,迁移毫无压力。 阅读详情

640?wx_fmt=jpeg

协程和通道是 Go 语言作为并发编程语言最为重要的特色之一,初学者可以完全将协程理解为线程,但是用起来比线程更加简单,占用的资源也更少。通常在一个进程里启动上万个线程就已经不堪重负,但是 Go 语言允许你启动百万协程也可以轻松应付。如果把协程比喻成小岛,那通道就是岛屿之间的交流桥梁,数据搭乘通道从一个协程流转到另一个协程。通道是并发安全的数据结构,它类似于内存消息队列,允许很多的协程并发对通道进行读写。

640?wx_fmt=png
图片

Go 语言里面的协程称之为 goroutine,通道称之为 channel。

协程的启动

Go 语言里创建一个协程非常简单,使用 go 关键词加上一个函数调用就可以了。Go 语言会启动一个新的协程,函数调用将成为这个协程的入口。

package main

import "fmt"
import "time"

func main() {
    fmt.Println("run in main goroutine")
    go func() {
        fmt.Println("run in child goroutine")
        go func() {
            fmt.Println("run in grand child goroutine")
            go func() {
                fmt.Println("run in grand grand child goroutine")
            }()
        }()
    }()
    time.Sleep(time.Second)
    fmt.Println("main goroutine will quit")
}

-------
run in main goroutine
run in child goroutine
run in grand child goroutine
run in grand grand child goroutine
main goroutine will quit

值得注意的是这里的 go 关键字语法和前面的 defer 关键字语法是一样的,它后面跟了一个匿名函数,然后还要带上一对(),表示对匿名函数的调用。

上面的代码中主协程睡眠了 1s,等待子协程们执行完毕。如果将睡眠的这行代码去掉,将会看不到子协程运行的痕迹

-------------
run in main goroutine
main goroutine will quit

子协程异常退出

在使用子协程时一定要特别注意保护好每个子协程,确保它们正常安全的运行。因为子协程的异常退出会将异常传播到主协程,直接会导致主协程也跟着挂掉,然后整个程序就崩溃了。

package main

import "fmt"
import "time"

func main() {
    fmt.Println("run in main goroutine")
    go func() {
        fmt.Println("run in child goroutine")
        go func() {
            fmt.Println("run in grand child goroutine")
            go func() {
                fmt.Println("run in grand grand child goroutine")
                panic("wtf")
            }()
        }()
    }()
    time.Sleep(time.Second)
    fmt.Println("main goroutine will quit")
}

---------
run in main goroutine
run in child goroutine
run in grand child goroutine
run in grand grand child goroutine
panic: wtf

goroutine 34 [running]:
main.main.func1.1.1()
    /Users/qianwp/go/src/github.com/pyloque/practice/main.go:14 +0x79
created by main.main.func1.1
    /Users/qianwp/go/src/github.com/pyloque/practice/main.go:12 +0x75
exit status 2

为了保护子协程的安全,通常我们会在协程的入口函数开头增加 recover() 语句来恢复协程内部发生的异常,阻断它传播到主协程导致程序崩溃。

go func() {
  if err := recover(); err != nil {
    // log error
  }
  // do something
}()

启动百万协程

Go 语言能同时管理上百万的协程,这不是吹牛,下面我们就来编写代码跑一跑这百万协程,读者们请想象一下这百万大军同时奔跑的感觉。

package main

import "fmt"
import "time"

func main() {
    fmt.Println("run in main goroutine")
    i := 1
    for {
        go func() {
            for {
                time.Sleep(time.Second)
            }
        }()
        if i % 10000 == 0 {
            fmt.Printf("%d goroutine started\n", i)
        }
        i++
    }
}

协程死循环

前面我们通过 recover() 函数可以防止个别协程的崩溃波及整体进程。但是如果有个别协程死循环了会导致其它协程饥饿得到不运行么?下面我们来做一个实验

package main

import "fmt"
import "time"

func main() {
    fmt.Println("run in main goroutine")
    n := 3
    for i:=0; i<n; i++ {
        go func() {
            fmt.Println("dead loop goroutine start")
            for {}  // 死循环
        }()
    }
    for {
        time.Sleep(time.Second)
        fmt.Println("main goroutine running")
    }
}

协程的本质

一个进程内部可以运行多个线程,而每个线程又可以运行很多协程。线程要负责对协程进行调度,保证每个协程都有机会得到执行。当一个协程睡眠时,它要将线程的运行权让给其它的协程来运行,而不能持续霸占这个线程。同一个线程内部最多只会有一个协程正在运行。

640?wx_fmt=png
图片

线程的调度是由操作系统负责的,调度算法运行在内核态,而协程的调用是由 Go 语言的运行时负责的,调度算法运行在用户态。

协程可以简化为三个状态,运行态、就绪态和休眠态。同一个线程中最多只会存在一个处于运行态的协程,就绪态的协程是指那些具备了运行能力但是还没有得到运行机会的协程,它们随时会被调度到运行态,休眠态的协程还不具备运行能力,它们是在等待某些条件的发生,比如 IO 操作的完成、睡眠时间的结束等。

640?wx_fmt=png
图片

操作系统对线程的调度是抢占式的,也就是说单个线程的死循环不会影响其它线程的执行,每个线程的连续运行受到时间片的限制。

Go 语言运行时对协程的调度并不是抢占式的。如果单个协程通过死循环霸占了线程的执行权,那这个线程就没有机会去运行其它协程了,你可以说这个线程假死了。不过一个进程内部往往有多个线程,假死了一个线程没事,全部假死了才会导致整个进程卡死。

每个线程都会包含多个就绪态的协程形成了一个就绪队列,如果这个线程因为某个别协程死循环导致假死,那这个队列上所有的就绪态协程是不是就没有机会得到运行了呢?Go 语言运行时调度器采用了 work-stealing 算法,当某个线程空闲时,也就是该线程上所有的协程都在休眠(或者一个协程都没有),它就会去其它线程的就绪队列上去偷一些协程来运行。也就是说这些线程会主动找活干,在正常情况下,运行时会尽量平均分配工作任务。

设置线程数

默认情况下,Go 运行时会将线程数会被设置为机器 CPU 逻辑核心数。同时它内置的 runtime 包提供了 GOMAXPROCS(n int) 函数允许我们动态调整线程数,注意这个函数名字是全大写,Go 语言的设计者就是这么任性,该函数会返回修改前的线程数,如果参数 n <=0 ,就不会产生修改效果,等价于读操作。

package main

import "fmt"
import "runtime"

func main() {
    // 读取默认的线程数
    fmt.Println(runtime.GOMAXPROCS(0))
    // 设置线程数为 10
    runtime.GOMAXPROCS(10)
    // 读取当前的线程数
    fmt.Println(runtime.GOMAXPROCS(0))
}

--------
4
10

package main

import "fmt"
import "time"
import "runtime"

func main() {
    fmt.Println(runtime.NumGoroutine())
    for i:=0;i<10;i++ {
        go func(){
            for {
                time.Sleep(time.Second)
            }
        }()
    }
    fmt.Println(runtime.NumGoroutine())
}

------
1
11

协程的应用

在日常互联网应用中,Go 语言的协程主要应用在HTTP API 应用、消息推送系统、聊天系统等。

在 HTTP API 应用中,每一个 HTTP 请求,服务器都会单独开辟一个协程来处理。在这个请求处理过程中,要进行很多 IO 调用,比如访问数据库、访问缓存、调用外部系统等,协程会休眠,IO 处理完成后协程又会再次被调度运行。待请求的响应回复完毕后,链接断开,这个协程的寿命也就到此结束。

在消息推送系统中,客户端的链接寿命很长,大部分时间这个链接都是空闲状态,客户端会每隔几十秒周期性使用心跳来告知服务器你不要断开我。在服务器端,每一个来自客户端链接的维持都需要单独一个协程。因为消息推送系统维持的链接普遍很闲,单台服务器往往可以轻松撑起百万链接,这些维持链接的协程只有在推送消息或者心跳消息到来时才会变成就绪态被调度运行。

聊天系统也是长链接系统,它内部来往的消息要比消息推送系统频繁很多,限于 CPU 和 网卡的压力,它能撑住的连接数要比推送系统少很多。不过原理是类似的,都是一个链接由一个协程长期维持,连接断开协程也就消亡。

在后面的高级内容部分,我将会教读者使用协程来实现上面这三个系统。下一章节我们开讲通道,因为通道的使用比较复杂,知识点较多,所以需要单独一节来讲。

640?wx_fmt=png

阅读《快学 Go 语言》更多章节,长按图片识别二维码关注公众号「码洞」

Go-一个基于golang实现的协程安全的mysqlbuilder 一个基于golang实现的协程安全的mysql builder 立即下载

相关推荐

如何制作poster以及下载poster模板

目录 前言 一、poster 二、制作软件 1.常用软件 2.专用工具 3.模板下载网址 总结 前言 随着会议的发展,参与国际会议成为研究工作展示的重要途径。poster逐步成为一个方面专家交流的重要工具。本文主要简单介绍一些poster的制作工具。 一、poster Paper, presentation, poster是deadline期间作者的三座大山。poster的内容较小,但内容都是论文中的干货。 二、制作软件 一般来说,制作poster使用PowerPoint.

yiyang14的博客 2万+

快速学习Go语言

if语句if语句用于判断某个条件是否成立,如果条件成立则执行if语句块内的代码,否则不执行。语法结构如下:// if 语句块 } else if condition2 {// else if 语句块 } else {// else 语句块 }switch语句switch语句用于根据不同的条件执行不同的代码块。语法结构如下:case value1 : // case1 语句块 case value2 : // case2 语句块 ... default : // default 语句块 }

qq_30245525的博客 2335

4.2 结构翻模

结构翻模功能可以将DWG文件中的二维图线对象:轴网、墙、柱、梁、基础、房间和面积等快速转换成Revit模型对象,翻模速度快,准确率高。与先前的模盒翻模功能不同,结构翻模不再需要在AutoCAD中提取模型的图线,所有操作在Revit均可完成。(1)翻模程序适用于钢筋混凝土结构的平法DWG文件。(2)将平法中所表示的轴网线,轴网文字,结构柱,梁,结构墙在Revit 中重新建出来。(3) 模盒可以翻出连...

fishbuaa的专栏 1798

go语言的main函数

main函数不能带参数 main函数不能定义返回值 main函数所在的包必须为main包 main函数中可以使用flag包来获取和解析命令行参数

1万+

新手避坑指南:HT7036计量芯片SPI读取电压的完整流程(从复位到校准)

本文详细介绍了HT7036计量芯片的SPI读取电压完整流程,从硬件准备到校准设置,重点解析了复位引脚处理、SPI通信配置、芯片初始化及数据处理等关键步骤。针对常见问题如晶振焊接、SPI时序错误等提供实用解决方案,帮助开发者高效完成电压测量与校准。

weixin_30070339的博客 327

基于Android13的系统启动流程分析(三)之FirstStageMain阶段

init进程是Android系统中用户空间的第一个进程,作为第一个进程,它被赋予了很多极其重要的工作职责,比如创建zygote(孵化器)和属性服务等。init进程是由多个源文件共同组成的,这些文件位于源码目录system/core/init1. Bootloader 引导当按下设备电源键时,最先运行的就是 bootloader(固化在ROM的程序),bootloader 的主要作用就是硬件设备(如 CPU、flash、内存)的初始化并加载到RAM,通过建立内存空间映射,为装载 Linux 内核做好准备,。

q1210249579的博客 5496

go协程实例-2

package main import ( "fmt" ) type worker struct { channel chan int done chan bool } func main() { var workers [10]worker for i:=0;i&lt;10 ;i++ { workers[i]=worker{ make(chan int), ...

qq_20867981的博客 435

ArcGIS-裁剪遥感影像及dem图像

6、选择【工具箱】-【数据管理工具】-【栅格】-【裁剪】。在【输入栅格】里选择要裁剪的dem,在【输出范围】里选择刚创建的裁剪范围。选择一个输出路径,其他参数保存默认。2、起一个名称,要素类型选择【面】。点击【编辑】选择与要裁剪的dem一致的地理坐标系。1、创建需要裁剪区域的面层shp,右击选择【新建】-【shapefile】3、点击编辑器,选择开始编辑。4、选择【裁剪范围】,点击确定。5、画出想要裁剪的范围并保存。

siwenaiyou的博客 1万+

关于Golang使用channel的死锁问题

发现问题 小白一枚,最近在入门学习Golang,在学到channel时,发现总会遇到死锁的问题: fatal error: all goroutines are asleep - deadlock! 原理解读 通过查找相关资料发现,channel本身是用于不同协程间通信的,一般一个协程作为生产者,另一个作为消费者,遵循下面两个原则: 当上一次生产到channel的数据未被消费时,生产者继...

程序猿的世界 1343

Office 365中的Assigned Access详细功能介绍

本文系统介绍了Office365中AssignedAccess功能的应用与管理。作为Windows系统的重要安全特性,AssignedAccess可将设备锁定为单应用模式,有效防范未授权访问。文章详细阐述了其核心功能、技术架构及配置方法,包括UWP/Win32应用支持、多用户配置、与AzureAD集成等。通过MicrosoftEndpointManager可实现批量部署和远程监控,适用于公共终端、零售设备等场景。该功能为企业数字化转型提供了安全、可控的终端管理方案,在保障数据安全的同时优化用户体验。

xueyunshengling的博客 227

搭建问答系统

数据描述 import docx import jieba from gensim.models import Word2Vec import os import numpy as np import pickle as pk import warnings from copy import deepcopy warnings.filterwarnings("ignore") from mu...

微信号:RunsenLiu 1842

《快学 Go 语言》第 6 课 —— 字典

字典在数学上的词汇是映射,将一个集合中的所有元素关联到另一个集合中的部分或全部元素,并且只能是一一映射或者多对一映射。 数组切片让我们具备了可以操作一块连续内存的能力,它是对同质元素的统一管理。而字典则赋予了不连续不同类的内存变量的关联性,它表达的是一种因果关系,字典的 key 是因,字典的 value 是果。如果说数组和切片赋予了我们步行的能力,那么字...

weixin_33813128的博客 142

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

千军万马跑协程goroutine

参考:https://cloud.tencent.com/developer/article/1375753 协程和通道是 Go 语言作为并发编程语言最为重要的特色之一,初学者可以完全将协程理解为线程,但是用起来比线程更加简单,占用的资源也更少。通常在一个进程里启动上万个线程就已经不堪重负,但是 Go 语言允许你启动百万协程也可以轻松应付。如果把协程比喻成小岛,那通道就是岛屿之间的...

rs勿忘初心的博客 325

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

TIOBE编程语言全球热度排行——2020年4月流行榜,Scratch成最大亮点

TIOBE编程语言全球热度榜,4月流行榜单刚刚发布。 哪种语言是最好、最合适的编程语言,一直是程序员和企业技术选型争论不休的话题。全世界的编程语言很多,其流行度变化莫测,但是每个时期流行的不过几十种,百名之外的编程语言对使用它的程序员和企业而言生存空间已经很小。因此,随时了解和掌握编程语言的热度、受欢迎程度,关乎程序员的技术栈储备和他的职业生涯,关乎项目和产品的技术选型以及今后的生命周期。下面是...

AliveCode,Code Life 1896

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

[AI智能体与提效-154] - 在这个新时代,Python 是唯一的桥梁。它一端连接着人类温暖模糊的自然语言,另一端连接着机器冰冷精确的二进制世界。通过这座桥梁,我们终于实现了科幻小说中的愿景。

在这个新时代,Python 是唯一的桥梁。它一端连接着人类温暖模糊的自然语言,另一端连接着机器冰冷精确的二进制世界。通过这座桥梁,我们终于实现了科幻小说中的愿景:像与人交谈一样,通过自然语言指挥千军万马般的计算资源。一、跨越“语义鸿沟”:从模糊意图到精确逻辑人类的自然语言是的。当我们说“帮我分析一下这个趋势”时,这句话包含了情感、语境、隐含假设和不确定性。这种“温暖”和“模糊”是人类创造力的源泉,却是传统计算机科学的噩梦。。Python 与自然语言的句法结构高度同构。

文火冰糖(王文兵)的博客 146

12月3日云栖精选夜读 | Python程序员的30个常见错误

在这篇文章中,我将总结新老Python程序员常犯的一些错误,以帮助你们在自己的工作避免犯同样或类似错误。 热点热议 Python程序员的30个常见错误 作者:技术小能手发表在:大数据地盘 在阿里,不是每一个项目都有KPI 作者:技术小能手 12月1日:阿里巴巴对“父老乡亲”有话说 作者:技术小能手发表在:阿里巴巴公益 知识整理 《快学 Go 语言...

weixin_34248118的博客 270

可观测性体系搭建:OpenTelemetry 与 Prometheus 集成

想象你刚搬进新家,但装修时没预留监控摄像头。当水管突然爆裂或电路过载时,你只能通过砸墙找问题。这就是传统监控系统的大致状态——被动等待故障发生,而非主动预防。现代系统就像拥有千军万马的大型企业,每个服务模块都是独立运作的士兵。OpenTelemetry和Prometheus组成的可观测性体系,相当于给每个岗位配备了智能手环+电子看板。前者实时追踪员工(服务)的生理指标(性能数据),后者则汇总全局数据生成作战地图(监控面板)。

2501_92585248的博客 1228

RPCS3模拟器技术原理与Windows高性能调优指南

模拟器是实现跨平台游戏运行的核心技术,其本质是通过软件精确复现目标硬件的行为逻辑。RPCS3作为PS3模拟器的标杆,依托C++实现对Cell处理器与RSX显卡的深度建模,涵盖指令译码、内存映射、DMA调度及GPU命令流翻译等底层机制。该技术价值在于突破原生平台限制,在现代PC上重建高保真运行环境,支撑怀旧体验、兼容性验证与系统级编程学习。典型应用场景包括PS3游戏在Windows平台的稳定渲染、DLC/UPD集成管理及音频-画面同步优化。本文聚焦RPCS3在Windows下的工程实践,深入解析Vulkan后

weixin_29174013的博客 210

Golang百万级高并发实践

写在前面Go语言作为新兴的语言,最近发展势头很是迅猛,其最大的特点就是原生支持并发。它使用的是“协程(goroutine)模型”,和传统基于 OS 线程和进程实现不同,Go 语言的并发是基于用户态的并发,这种并发方式就变得非常轻量,能够轻松运行几万并发逻辑。Go 的并发属于 CSP 并发模型的一种实现,CSP 并发模型的核心概念是:“不要通过共享内存来通信,而应该通 过通信来共享内存”。这在 G

Jeanphorn的专栏 3万+

go 开了多少个goroutine 怎么看_Go语言进阶之路(五):通道、goroutine和GPM

通道Go语言可以有效地利用多核CPU,并发性能好,这正是由于goroutine和通道还有GPM模型的原因。我们知道,Python语言由于全局锁GIL,单个Python应用的多线程代码没办法有效利用多核CPU,只能写多进程来利用多核CPU。如果用标准库的multiprocessing会对监控和管理造成不少的挑战。部署Python应用的时候通常是每个CPU核部署一个应用,这样会浪费不少资源。...

weixin_33842969的博客 678
上一篇: 老大难的 Java ClassLoader,到了该彻底理解它的时候了
下一篇: 码洞原创深度技术文章大全 —— 高端面试必备
codehole_
博客等级 码龄7年 34粉丝 132原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值