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 函数中的循环会一直运行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/168808.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

部署系列六基于nndeploy的深度学习 图像降噪unet部署

文章目录 1.直接在源代码demo中修改2. 如何修改呢&#xff1f;3. 修改 graph4. 总结 https://github.com/DeployAI/nndeploy https://nndeploy-zh.readthedocs.io/zh/latest/introduction/index.html 通过以上2个官方链接对nndeploy基本的使用方法应该有所了解了。 下面就是利用…

自动语音识别 支持86种语言 Dragon Professional 16 Crack

从个体从业者到全球组织&#xff0c;文档密集型行业的专业人士长期以来一直依靠 Dragon 语音识别来更快、更高效地创建高质量文档&#xff0c;减少管理开销&#xff0c;以便他们能够专注于客户。了解 Dragon Professional v16 如何通过单一解决方案提高标准&#xff0c;为各个业…

ArcGis如何用点连线?

这里指的是根据已有坐标点手动连线&#xff0c;类似于mapgis中的“用点连线”&#xff0c;线的每个拐点是可以自动捕捉到坐标点的&#xff0c;比直接画精确。 我也相信这么强大的软件一定可以实现类似于比我的软件上坐标时自动生成的线&#xff0c;但是目前我还没接触到那里&a…

基于IDEA+SpringBoot+微服务开发的P2P平台项目

基于springboot的社区养老医疗综合服务平台 项目介绍&#x1f481;&#x1f3fb; 项目名称&#xff1a;基于P2P的金融项目 一个基于P2P&#xff08;点对点&#xff09;模式的金融服务平台&#xff0c;致力于提供透明、高效、安全的金融服务。我们的目标是连接借款人与投资者&am…

Clion在Windows下build时出现undefined reference,即使cmake已经正确链接第三方库(如protobuf)?

你是否正在使用clion自带的vcpkg来安装了protobuf&#xff1f; 或者你是否自己使用visual studio自己编译了libprotobuf.lib&#xff1f; 你是否已经正确在CmakeLists.txt中添加了以下命令&#xff1a; find_package(Protobuf CONFIG REQUIRED) include_directories(${Protobu…

inBuilder低代码平台新特性推荐-第十期

各位知乎的友友们&#xff0c;大家好~ 今天来给大家带来的是inBuilder低代码平台特性推荐系列第十期——查看变更日志 场景介绍 【销售订单列表】中添加查看变更日志按钮&#xff0c;可以查看列表当前行数据的历史变更记录。 运行时效果 概念 系统中有些关键业务关键数据&am…

【极客技术】真假GPT-4?微调 Llama 2 以替代 GPT-3.5/4 已然可行!

近日小编在使用最新版GPT-4-Turbo模型&#xff08;主要特点是支持128k输入和知识库截止日期是2023年4月&#xff09;时&#xff0c;发现不同商家提供的模型回复出现不一致的情况&#xff0c;尤其是模型均承认自己知识库达到2023年4月&#xff0c;但当我们细问时&#xff0c;Fak…

如何开发有趣而富有创意的营销小游戏

在数字化时代&#xff0c;企业通过创意而独特的方式与目标受众互动&#xff0c;已成为提高品牌知名度和用户参与度的重要手段之一。其中&#xff0c;设计一款引人入胜的营销小游戏&#xff0c;不仅能吸引用户的眼球&#xff0c;还能有效传达品牌信息。以下是一些建议&#xff0…

AI赋能数据表设计

数据表设计软件用过多种&#xff0c;用Ai 设计表几年Ai大模型爆发之后提升了新的高度 用navicat 设计表就是在跟团队的人介绍这次功能的表结构时&#xff0c;没办法看备注&#xff0c;只能看英文字段&#xff0c;导致在比较复杂的表中&#xff0c;总是在表结构和图形结构中来回…

转录组学习第5弹-比对参考基因组

比对参考基因组 在构建文库的过程中需要将DNA片段化&#xff0c;因此测序得到的序列只是基因组的部分序列。为了确定测序reads在基因组上的位置&#xff0c;需要将reads比对回参考基因组上&#xff0c;这个步骤叫做比对&#xff0c;即文献中所提到的alignment或mapping。包括基…

2023.11.23使用flask实现在指定路径生成文件夹操作

2023.11.23使用flask实现在指定路径生成文件夹操作 程序比较简单&#xff0c;实现功能&#xff1a; 1、前端输入文件夹 2、后端在指定路径生成文件夹 3、前端反馈文件夹生成状态 main.py from flask import Flask, request, render_template import osapp Flask(__name__)a…

SAP从放弃到入门系列之-制造商零件编号-MPN 物料

文章目录 一、概念二、 配置点配置点1&#xff1a;启用MPN配置点2&#xff1a;MPN配置参数文件配置点3&#xff1a;激活库存管理的MPN所有功能变化1&#xff1a;MM01界面有库存管理制造商零部件号的字段&#xff1a;变化2&#xff1a;MM60界面的查询条件多了MPN物料号变化3&…

Mac开发环境——MacOSX安装与配置Anaconda与PyCharm详细流程

一、安装与使用Anaconda 1.简介 Anaconda 是一个用于数据科学、机器学习和科学计算的开源发行版和包管理器。有许多可用于数据处理、分析和建模的工具和库&#xff0c;并提供了一个方便的环境管理系统。Anaconda 包含了 Python 解释器和许多常用的 Python 包&#xff0c;以及…

【Unity入门】碰撞检测

碰撞器由来 1.系统默认会给每个对象(GameObject)添加一个碰撞组件(ColliderComponent)&#xff0c;一些背景对象则可以取消该组件。 2.在unity3d中&#xff0c;能检测碰撞发生的方式有两种&#xff0c;一种是利用碰撞器&#xff0c;另一种则是利用触发器。这两种方式的应用非…

Android Studio 显示build variants工具栏

工具栏&#xff1a; 如下图所示 依次点击View-->ToolWindows-->Build Variants。 在此记个笔记

Spring原理——基于xml配置文件创建IOC容器的过程

Spring框架的核心之一是IOC&#xff0c;那么我们是怎么创建出来的Bean呢&#xff1f; 作者进行了简单的总结&#xff0c;希望能对你有所帮助。 IOC的创建并不是通过new而是利用了java的反射机制&#xff0c;利用了newInstance方法进行的创建对象。 首先&#xff0c;我们先定义…

数据结构 | 堆【图解】

数据结构 | 堆【图解】 文章目录 数据结构 | 堆【图解】堆的概念及结构堆的实现堆的初始化堆的插入【重点】堆的删除【重点】取堆顶的数据堆的数据个数堆的判空堆的销毁 全部代码 堆的概念及结构 堆&#xff08;heap&#xff09;&#xff1a; 一种有特殊用途的数据结构——用来…

详解:什么是“智能合同管理”

未来已来&#xff0c;行业数字化进行的如火如荼&#xff0c;并逐步驶入深水区。合同是企业开展经营活动的重要文件&#xff0c;也是风险管控的核心地带&#xff0c;做好合同管理对企业运营效率的提升至关重要。近年来&#xff0c;合同管理已经跟随企业数字化的浪潮进入转型时期…

Leetcode—2824.统计和小于目标的下标对数目【简单】

2023每日刷题&#xff08;三十九&#xff09; Leetcode—2824.统计和小于目标的下标对数目 实现代码 class Solution { public:int countPairs(vector<int>& nums, int target) {int n nums.size();sort(nums.begin(), nums.end());int left 0, right left 1;i…

迈巴赫S480升级电动后门 手势控制开关 更加方便

安装了电动后门的迈巴赫S480&#xff0c;从原来的触摸门把手和门内拉手开关门&#xff0c;增加了钥匙控制、前排显示屏控制、后门按键开关控制、后排娱乐屏控制等多种开关门方式&#xff0c;将一个待客之礼体现出多种不一样的尊贵感受。 中控显示屏由驾驶者控制&#xff0c;可以…