当前位置: 首页 > news >正文

Golang并发模型:Goroutine 与 Channel 初探

文章目录

    • goroutine
      • goexit()
    • channel
      • 缓冲
      • close
      • range
      • select

goroutine

goroutine 是 Go 语言中的一种轻量级线程(lightweight thread),由 Go 运行时环境管理。与传统的线程相比,goroutine 的创建和销毁的开销很小,可以轻松创建成千上万个 goroutine,而不会导致系统性能下降。

以下是一些关于 goroutine 的重要特性:

  1. 轻量级: 每个 goroutine 的栈大小初始时只有几 KB,可以根据需要进行动态扩展和收缩。

  2. 并发执行: Go 语言通过 goroutine 实现并发编程,允许在程序中同时执行多个任务。通过 goroutine,可以更方便地编写并发程序。

  3. 独立调度: Go 运行时具有自己的调度器,它负责在多个 goroutine 之间进行协作式调度。与操作系统线程不同,goroutine 的调度是在用户空间进行的,减少了上下文切换的成本。

  4. 通信通过通道: 多个 goroutine 之间通过通道进行通信。通道是一种数据结构,用于在 goroutine 之间传递数据。通过使用通道,可以更安全、更简单地实现 goroutine 之间的通信和同步。

以下是一个简单的示例,演示了如何创建和运行一个 goroutine:

package mainimport ("fmt""time"
)// 子goroutine
func newTask() {for i := 0; ; i++ {fmt.Printf("new goroutine:i = %d\n", i)time.Sleep(1 * time.Second)}
}// 主goroutine
func main() {// 创建一个go程 去执行newTask()流程go newTask()for i := 0; ; i++ {fmt.Printf("main goroutine:i = %d\n", i)time.Sleep(1 * time.Second)}
}

由运行结果可以看出,newTask 函数被启动为一个 goroutine,与主 goroutine 同时执行。由于 goroutine 是并发执行的,输出将交替显示两个 goroutine 的执行结果。
请添加图片描述
但是,当主goroutine执行任务完成退出比子goroutine早时,程序就会直接终止,而不再等待其他的 goroutine 执行完毕。如以下代码所示。

package mainimport ("fmt""time"
)// 子goroutine
func newTask() {for i := 0; ; i++ {fmt.Printf("new goroutine:i = %d\n", i)time.Sleep(1 * time.Second)}
}// 主goroutine
func main() {// 创建一个go程 去执行newTask()流程go newTask()fmt.Println("main goroutine exit")
}
/*输出结果
main goroutine exit
*/

goexit()

runtime.Goexit() 是 Go 语言 runtime 包提供的一个函数,用于立即终止当前 goroutine 的执行。调用 Goexit 会导致当前 goroutine 进行善后工作(执行已注册的 defer 语句)并返回,而不会影响其他正在运行的 goroutine。

package mainimport ("fmt""runtime""time"
)func main() {// 用go创建一个形参为空,返回值为空的函数go func() {defer fmt.Println("A.defer")func() {defer fmt.Println("B.defer")runtime.Goexit()fmt.Println("B")}()fmt.Println("A")}()for {time.Sleep(1 * time.Second)}
}
/*输出结果
B.defer
A.defer
*/

在 goroutine 内部的第二个 defer 语句 defer fmt.Println("B.defer") 表示在该 goroutine 返回之前执行。然后,在这个 goroutine 内部的匿名函数中调用了 runtime.goexit(),这会立即结束当前 goroutine 的执行。

由于 runtime.goexit()的调用,后面的代码不再执行,包括 “B” 的打印语句和 “A” 的打印语句。

channel

在Go语言中,channel 是一种用于在 goroutine 之间进行通信和同步的数据结构。它提供了一种安全、简单且高效的方式,使得不同的 goroutine 之间能够传递数据和同步操作。

channel的定义:c := make(chan int)

下面是进行 goroutine 间通信的基本例子。因为 channel 是阻塞的,所以在主 goroutine 中接收数据的操作 num := <-c 会等待直到 goroutine 发送完数据为止。

package mainimport "fmt"func main() {c := make(chan int)go func() {defer fmt.Println("goroutine end")fmt.Println("goroutine正在运行")c <- 666 //将666发送给c}()num := <-c //从c中接收数据,并赋值给numfmt.Println("num =", num)fmt.Println("main goroutine end")
}
/*输出结果
goroutine正在运行
goroutine end
num = 666
main goroutine end
*/

缓冲

上面例子中的channel是一个无缓冲的,其运行的具体过程如下图所示。
请添加图片描述
在给channel加上缓冲后,运行过程如下图。
请添加图片描述

下面这段代码演示了使用带有缓冲的 channel 进行 goroutine 间通信的例子。由于通道带有缓冲,即使没有立即被接收,子 goroutine 依然可以向通道发送多个数据,直到达到缓冲容量。在主 goroutine 中,使用缓冲通道可以使得发送和接收操作更灵活,不需要等待另一方立即接收。

package mainimport ("fmt""time"
)func main() {c := make(chan int, 3) // 带有缓冲的channelfmt.Println("len(c) =", len(c), "cap(c) =", cap(c))go func() {defer fmt.Println("子go程结束")for i := 0; i < 3; i++ {c <- ifmt.Println("子go程正在运行,len(c) =", len(c), "cap(c) =", cap(c))}}()time.Sleep(2 * time.Second)for i := 0; i < 3; i++ {num := <-cfmt.Println("num =", num)}fmt.Println("主go程结束")
}
/*运行结果
len(c) = 0 cap(c) = 3
子go程正在运行,len(c) = 1 cap(c) = 3
子go程正在运行,len(c) = 2 cap(c) = 3
子go程正在运行,len(c) = 3 cap(c) = 3
子go程正在运行,len(c) = 3 cap(c) = 3
子go程结束
num = 0
num = 1
num = 2
num = 3
主go程结束
*/

close

close 函数用于关闭一个通道。

package mainimport "fmt"func main() {c := make(chan int)go func() {for i := 0; i < 3; i++ {c <- i}close(c) // close可以关闭一个channel}()for {// ok如果为true表示channel没有关闭,如果为false表示已经关闭if data, ok := <-c; ok {fmt.Println(data)} else {break}}fmt.Println("main goroutine end")
}

channel无需像文件一样经常关闭,只有确实不需要发数据了,或者想显式结束range循环,才去关闭channel。
关闭channel后,无法再向channel发送数据,但是可以继续接收数据。
对于nil channel,无论收发都会被阻塞。

range

当使用 range 迭代通道时,它会一直阻塞,直到通道关闭且所有的元素都被读取完毕。需要注意的是,在使用 range 迭代通道时,通道必须在某个地方被关闭,否则 range 不会结束。否则,它会一直等待新的数据,导致程序阻塞

package mainimport "fmt"func main() {c := make(chan int)go func() {for i := 0; i < 3; i++ {c <- i}close(c) // close可以关闭一个channel}()// 可以使用range来迭代不断操作channelfor data := range c {fmt.Println(data)}fmt.Println("main goroutine end")
}

select

单流程下一个go只能监控一个channel的状态,select可以完成监控多个channel

基本语法:

select {
case <- chan1:// 如果chan1成功读取到数据,就会进入该case并处理
case chan2 <- 1:// 如果成功向chan2写入数据,就会进入该case并处理
default:// 如果上面都没有成功,就进入default处理
}

下面是一个通过 Go 语言的 select 语句和通道(channel)来生成斐波那契数列的例子。

package mainimport "fmt"func fibonacii(c, quit chan int) {x, y := 1, 1for {select {case c <- x:// 如果c可写,那么就会进入该casex = yy = x + ycase <-quit:fmt.Println("quit")return}}
}func main() {c := make(chan int)quit := make(chan int)go func() {for i := 0; i < 10; i++ {fmt.Println(<-c)}quit <- 0}()fibonacii(c, quit)
}

这个程序的输出是斐波那契数列的前 10 个数字,然后输出 “quit”。由于 main 函数中的 goroutine 先执行,因此在 quit 通道被关闭之前,fibonacci 函数中的循环会一直运行。

相关文章:

Golang并发模型:Goroutine 与 Channel 初探

文章目录 goroutinegoexit() channel缓冲closerangeselect goroutine goroutine 是 Go 语言中的一种轻量级线程&#xff08;lightweight thread&#xff09;&#xff0c;由 Go 运行时环境管理。与传统的线程相比&#xff0c;goroutine 的创建和销毁的开销很小&#xff0c;可以…...

批量添加PPT备注

我一直都觉得&#xff0c;用python高效办公&#xff0c;是件没必要的事。。。 但直到最近写课做PPT&#xff0c;做了80多页PPT&#xff0c;要把每一页PPT的备注粘贴进去时 我觉得&#xff0c;有什么关系呢&#xff0c;一页一页粘 但是粘到5页&#xff0c;我感觉ctlc\v频率有点儿…...

数据挖掘之PCA-主成分分析

PCA的用处&#xff1a;找出反应数据中最大变差的投影&#xff08;就是拉的最开&#xff09;。 在减少需要分析的指标同时&#xff0c;尽量减少原指标包含信息的损失&#xff0c;以达到对所收集数据进行全面分析的目的 但是什么时候信息保留的最多呢&#xff1f;具体一点&#…...

人工智能-注意力机制之注意力汇聚:Nadaraya-Watson 核回归

查询&#xff08;自主提示&#xff09;和键&#xff08;非自主提示&#xff09;之间的交互形成了注意力汇聚&#xff1b; 注意力汇聚有选择地聚合了值&#xff08;感官输入&#xff09;以生成最终的输出。 本节将介绍注意力汇聚的更多细节&#xff0c; 以便从宏观上了解注意力机…...

<HarmonyOS第一课>1·运行Hello World【课后考核】

【习题】运行Hello World工程 判断题 1.DevEco Studio是开发HarmonyOS应用的一站式集成开发环境。 正确(True) 2.main_pages.json存放页面page路径配置信息。 正确(True) 单选题 1.在stage模型中&#xff0c;下列配置文件属于AppScope文件夹的是&#xff1f;&#xff08;…...

Ubuntu18.04安装A-Loam保姆级教程

系统环境&#xff1a;Ubuntu18.04.6 LTS 1.A-Loam的安装前要求&#xff1a; 1.1 ROS安装&#xff1a;参考我的另一篇博客 Ubuntu18.04安装ROS-melodic保姆级教程_灬杨三岁灬的博客-CSDN博客还是那句话&#xff0c;有时候加了这行也不好使&#xff0c;我是疯狂试了20次&#…...

重生之我是一名程序员 40 ——字符串函数(1)

哈喽啊大家晚上好&#xff01;今天呢给大家带来点新的东西——字符串函数strcpy。 首先&#xff0c;让我来给大家介绍一下它。strcpy函数是C语言中的一个字符串函数&#xff0c;用于将一个字符串复制到另一个字符串中。其函数原型为&#xff1a; char* strcpy(char* dest, co…...

Navicat 技术指引 | 连接 GaussDB 主备版

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…...

【git】pip install git+https://github.com/xxx/xxx替换成本地下载编译安装解决网络超时问题

目录 &#x1f311;&#x1f311; 背景 &#x1f312; &#x1f312;作用 &#x1f314;&#x1f314; 问题 &#x1f314;&#x1f314;解决方案 &#x1f319;方法一 &#x1f319;方法二 &#x1f31d;&#x1f31d;我的解决方案 整理不易&#xff0c;欢迎一键三连…...

SQL Server对象类型(6)——4.6.存储过程和函数(Procedure和Function)

4.6. 存储过程和函数(Procedure和Function) 4.6.1. 过程和函数概念 与Oracle中类似,SQL Server中,存储过程和函数都是虚的、被定义的代码对象,它们都是由一组T-SQL或公共语言运行库(CLR)代码构成的程序,其本身并不存储数据,通过数据库或应用端调用等方式来运行,以满…...

spring @Async异步执行

在一些后台耗时的场景比如说生成统计报表&#xff0c;生成数据文件&#xff0c;执行批量任务时候&#xff0c;需要异步执行&#xff0c;先登记信息&#xff0c;然后异步执行批量同步返回给客户端。在spring中要想使用异步方法执行&#xff0c;必须使用EnableAsync注解开启async…...

#Js篇:单线程模式同步任务异步任务任务队列事件循环setTimeout() setInterval()

单线程模式 之所以采用单线程&#xff0c;而不是多线程&#xff0c;跟历史有关系。原因是不想让浏览器变得太复杂&#xff0c;因为多线程需要共享资源、且有可能修改彼此的运行结果&#xff0c;对于一种网页脚本语言来说&#xff0c;太复杂了。 好处 实现起来比较简单&#…...

html table样式的设计 表格边框修饰

<!DOCTYPE html> <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8" /> <title>今日小说排行榜</title> <style> table {border-collapse: collapse;border: 4px double red; /*…...

2023年【危险化学品经营单位安全管理人员】考试内容及危险化学品经营单位安全管理人员最新解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 危险化学品经营单位安全管理人员考试内容是安全生产模拟考试一点通生成的&#xff0c;危险化学品经营单位安全管理人员证模拟考试题库是根据危险化学品经营单位安全管理人员最新版教材汇编出危险化学品经营单位安全管…...

腾讯云 小程序 SDK对象存储 COS使用记录,原生小程序写法。

最近做了一个项目&#xff0c;需求是上传文档&#xff0c;文档类型多种&#xff0c;图片&#xff0c;视频&#xff0c;文件&#xff0c;doc,xls,zip,txt 等等,而且文档类型可能是大文件&#xff0c;可能得上百兆&#xff0c;甚至超过1G。 腾讯云文档地址&#xff1a;https://c…...

【uniapp】本地资源图片无法通过 WXSS 获取,可以使用网络图片,或者 base64,或者使用image标签

uniapp开发 微信小程序 本地资源图片无法通过 WXSS 获取&#xff0c;可以使用网络图片&#xff0c;或者 base64&#xff0c;或者使用image标签。_uniapp 中的本地资源图片无法通过 wxss 获取,可以使用网络图片,或者 base64,或者_芒果大胖砸的博客-CSDN博客...

深入了解Spring Cloud中的分布式事务解决方案

引言 介绍分布式系统中事务管理的重要性&#xff0c;以及在云计算环境下分布式事务所面临的挑战。 传统事务和分布式事务 解释本地事务与分布式事务的区别&#xff0c;以及为什么在分布式环境中需要特殊的事务管理机制。 分布式事务的挑战 探讨在分布式系统中实现事务一致性所…...

安装compiler version 5

这个compiler version5 在我的资源里面可以免费下载&#xff1b; 另外这个东西还需要安装&#xff0c;安装教程在这里&#xff1a;Keil最新版保姆教程&#xff08;解决缺少V5编译器问题&#xff09; - 哔哩哔哩 (bilibili.com) 看吧安装好了year...

关闭vscode打开的本地服务器端口

vscode开了本地的一个端口“8443”当本地服务器端口&#xff0c;然后随手把VScode一关&#xff0c;后来继续做发现8443端口已经被占用了。   原来&#xff0c;即便关闭了编译器VScode&#xff0c;服务器依然是被node.exe运行着的。那这个端口怎么才能关掉呢&#xff1f;   …...

VUE3+Springboot实现SM2完整步骤

一.VUE3代码实现 1.安装依赖 npm install --save sm-crypto 2.导入sm2 const sm2 require(sm-crypto).sm2 3.定义公钥私钥 var privateKey "私钥";//解密使用 var publicKey "公钥";//加密使用 4.设置加密模式 //cipherMode [加密模式 C1C3C2:1,…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

力扣热题100 k个一组反转链表题解

题目: 代码: func reverseKGroup(head *ListNode, k int) *ListNode {cur : headfor i : 0; i < k; i {if cur nil {return head}cur cur.Next}newHead : reverse(head, cur)head.Next reverseKGroup(cur, k)return newHead }func reverse(start, end *ListNode) *ListN…...

python爬虫——气象数据爬取

一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用&#xff1a; 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests&#xff1a;发送 …...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

Visual Studio Code 扩展

Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后&#xff0c;命令 changeCase.commands 可预览转换效果 EmmyLua…...

32位寻址与64位寻址

32位寻址与64位寻址 32位寻址是什么&#xff1f; 32位寻址是指计算机的CPU、内存或总线系统使用32位二进制数来标识和访问内存中的存储单元&#xff08;地址&#xff09;&#xff0c;其核心含义与能力如下&#xff1a; 1. 核心定义 地址位宽&#xff1a;CPU或内存控制器用32位…...

背包问题双雄:01 背包与完全背包详解(Java 实现)

一、背包问题概述 背包问题是动态规划领域的经典问题&#xff0c;其核心在于如何在有限容量的背包中选择物品&#xff0c;使得总价值最大化。根据物品选择规则的不同&#xff0c;主要分为两类&#xff1a; 01 背包&#xff1a;每件物品最多选 1 次&#xff08;选或不选&#…...

python可视化:俄乌战争时间线关键节点与深层原因

俄乌战争时间线可视化分析&#xff1a;关键节点与深层原因 俄乌战争是21世纪欧洲最具影响力的地缘政治冲突之一&#xff0c;自2022年2月爆发以来已持续超过3年。 本文将通过Python可视化工具&#xff0c;系统分析这场战争的时间线、关键节点及其背后的深层原因&#xff0c;全面…...