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 main

import (
	"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 main

import (
	"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 main

import (
	"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 main

import "fmt"

func main() {
	c := make(chan int)

	go func() {
		defer fmt.Println("goroutine end")

		fmt.Println("goroutine正在运行")

		c <- 666 //将666发送给c
	}()

	num := <-c //从c中接收数据,并赋值给num

	fmt.Println("num =", num)
	fmt.Println("main goroutine end")
}
/*输出结果
goroutine正在运行
goroutine end
num = 666
main goroutine end
*/

缓冲

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

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

package main

import (
	"fmt"
	"time"
)

func main() {
	c := make(chan int, 3) // 带有缓冲的channel

	fmt.Println("len(c) =", len(c), "cap(c) =", cap(c))

	go func() {
		defer fmt.Println("子go程结束")

		for i := 0; i < 3; i++ {
			c <- i
			fmt.Println("子go程正在运行,len(c) =", len(c), "cap(c) =", cap(c))
		}
	}()

	time.Sleep(2 * time.Second)

	for i := 0; i < 3; i++ {
		num := <-c
		fmt.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 main

import "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 main

import "fmt"

func main() {
	c := make(chan int)

	go func() {
		for i := 0; i < 3; i++ {
			c <- i
		}
		close(c) // close可以关闭一个channel
	}()

	// 可以使用range来迭代不断操作channel
	for 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 main

import "fmt"

func fibonacii(c, quit chan int) {
	x, y := 1, 1
	for {
		select {
		case c <- x:
			// 如果c可写,那么就会进入该case
			x = y
			y = x + y
		case <-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 函数中的循环会一直运行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python基于jieba+wordcloud实现文本分词、词频统计、条形图绘制及不同主题的词云图绘制

目录 序言&#xff1a;第三方库及所需材料函数模块介绍分词词频统计条形图绘制词云绘制主函数 效果预览全部代码 序言&#xff1a;第三方库及所需材料 编程语言&#xff1a;Python3.9。 编程环境&#xff1a;Anaconda3&#xff0c;Spyder5。 使用到的主要第三方库&#xff1a;…

【Leetcode】【实现循环队列】【数据结构】

代码实现&#xff1a; typedef struct {int front;int back;int k;int* a;} MyCircularQueue;bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->frontobj->back; }bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->back1)%(obj->…

位图和布隆过滤器

目录 一. 位图 1.题目&#xff1a; 给40亿个不重复的无符号整数&#xff0c;没排过序。给一个无符号整数&#xff0c;如何快速判断一个数是否在这40亿个数中&#xff1f; 2.解析题目&#xff1a; 3.位图 4.代码以及测试 5.其他题目 二.布隆过滤器 1.介绍 2.实现 …

Vue服务端渲染——同构渲染

Vue.js 可以用于构建客户端应用程序&#xff0c;组件的代码在浏览器中运行&#xff0c;并输出 DOM 元素。同时&#xff0c;Vue.js 还可以在 Node.js 环境中运行&#xff0c;它可以将同样的组件渲染为字符串并发送给浏览器。这实际上描述了 Vue.js 的两种渲染方式&#xff0c;即…

【云原生】什么是 Kubernetes ?

什么是 Kubernetes &#xff1f; Kubernetes 是一个开源容器编排平台&#xff0c;管理着一系列的 主机 或者 服务器&#xff0c;它们被称作是 节点&#xff08;Node&#xff09;。 每一个节点运行了若干个相互独立的 Pod。 Pod 是 Kubernetes 中可以部署的 最小执行单元&#x…

电脑技巧:U盘运用小技巧,提升U盘运用寿命

目录 1、注意清洁&#xff0c;防止污染 2、别随意插拔 3、文件多时分段写入 4、U盘传输数据中切记拔掉U盘 5、建议不要长期将U盘插在电脑上 6、杜绝别频繁将U盘格式化 7、U盘中毒怎么办 U盘是大家日常办公经常用得到的便携式文件储存工具&#xff0c;因为其小巧便携、方…

5.3每日一题(不确定正负号的级数敛散性:和一个正项级数比较判定)

比较判别法和比较判别法的极限形式是对正项级数而言的&#xff0c;若一个级数和p级数比较&#xff0c;结果>0&#xff0c;则同敛散&#xff1b;若结果<0&#xff0c;则结果乘以-1 结果又同敛散了&#xff1b;所以只要比值不等于0&#xff0c;则同敛散&#xff1b; 所以当…

鸿蒙(HarmonyOS)应用开发——生命周期、渲染控制、状态管理装饰器

生命周期 任何程序都是有一定的生命周期的。生命周期是记录从产生到销毁的过程&#xff1b;如果熟悉前端vue.js的话&#xff0c;就可以很好的理解生命周期。 自定义组件生命周期 ArkTS中&#xff0c;自定义组件提供了两个生命周期函数&#xff1a;aboutToAppear() 和aboutTo…

代码随想录算法训练营第四十七天|198. 打家劫舍、213. 打家劫舍II、337. 打家劫舍III

LeetCode 198. 打家劫舍 题目链接&#xff1a;198. 打家劫舍 - 力扣&#xff08;LeetCode&#xff09; 第一次打家劫舍&#xff0c;来个简单一些的&#xff0c;无非就是偷了当前这家偷不了下一家&#xff0c;因此dp[n]代表&#xff0c;偷前n家的时候所能偷到的最高金额&#x…

记一次RocketMQ线上broker内存持续升高问题排查

RocketMQ 版本 5.1.0 jdk版本 1.8 JVM启动参数 -Xms46g -Xmx46g -XX:MetaspaceSize1259m -XX:MaxMetaspaceSize2517m -XX:UseG1GC -XX:G1HeapRegionSize16m -XX:G1ReservePercent25 -XX:InitiatingHeapOccupancyPercent30 -XX:SoftRefLRUPolicyMSPerMB0 -verbose:gc -Xlog…

【Docker】Docker与Kubernetes:区别与优势对比

前言 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。   kubernetes&#xff0c;简称K8s&a…

Nacos身份绕过漏洞复现(QVD-2023-6271)

Nacos身份绕过漏洞复现&#xff08;QVD-2023-6271&#xff09; 环境配置 该漏洞主要用了win10_JAVA的环境&#xff0c;参考网上已有的复现文章&#xff0c;使用jdk-11.0.2_windows-x64_bin.exe 由于2.2.0之后的nacos已将本漏洞修复&#xff0c;所以本次复现使用2.2.0的包 下…

cephadm部署ceph quincy版本

环境说明 IP主机名角色 存储设备 192.168.2.100 master100 mon,mgr,osd,mds,rgw 大于5G的空设备192.168.2.101node101mon,mgr,osd,mds,rgw大于5G的空设备192.168.2.102node102mon,mgr,osd,mds,rgw大于5G的空设备 关闭防火墙 关闭并且禁用selinux 配置主机名/etc/hosts …

深度学习之图像分类(十四)CAT: Cross Attention in Vision Transformer详解

IPSA和CPSA的处理流程、维度变换细节 FLOPs的计算方法、以及flops和划分的patch数目以及patch的维度计算关系 IPSA如何进行local attention、CPSA如何进行globe attention CAT的代码详细注释---需要学习完Transformer TNT、swin transformer、crossViT CAT: Cross Atten…

2023年【道路运输企业安全生产管理人员】最新解析及道路运输企业安全生产管理人员复审考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 道路运输企业安全生产管理人员最新解析是安全生产模拟考试一点通总题库中生成的一套道路运输企业安全生产管理人员复审考试&#xff0c;安全生产模拟考试一点通上道路运输企业安全生产管理人员作业手机同步练习。2023…

【Web】攻防世界 难度3 刷题记录(1)

目录 ①lottery ②ics-05 ③mfw ④simple_js ⑤fakebook 感觉自己对一些综合题的熟练度不太够&#xff0c;专项训练一下 ①lottery 抽奖赚钱&#xff0c;钱够9990000可买flag 随便输一串数字抓包&#xff0c;然后查看到一个post请求&#xff0c;api.php,题目里面有附件…

armbian折腾之docker搭建chatgptweb指导(无需魔法)

文章目录 前言面板/docker的安装获取中转Key创建docker容器chatgpt-next-web部署[推荐]chatgpt-Web部署 推荐学习openai-hk官方的部署指导 前言 好久都没有折腾armbian&#xff0c;导致吃了很长时间的灰&#xff0c;今天偶然看到B站UP主JeeJK007的搭建视频&#xff0c;便想着能…

电脑技巧:电脑常见蓝屏、上不了网等故障及解决办法

目录 一、电脑蓝屏 常见原因1: 病毒木马 常见原因2: 安装了不兼容的软件 二、电脑不能上网 常见原因1: 新装系统无驱动 常见原因2: DNS服务器异常 常见原因3: 硬件问题 三、电脑没声音 常见原因1: 未安装驱动 常见原因2: 硬件故障 四、电脑屏幕不显示 常见原因1: 显…

【Mybatis】基础增删改查

一.创建SpringBoot项目 创建新项目需要添加的依赖 当然如果是以前的项目也可以直接在pom.xml文件中添加依赖: MySQL Driver依赖 <dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</…

JVM虚拟机:G1垃圾回收器的日志分析

本文重点 本文我们将学习G1垃圾回收器的日志 使用 执行命令 java -Xms20M -Xmx20M -XX:PrintGCDetails -XX:UseG1GC 类名 分析 前面我们学习了G1垃圾回收器&#xff0c;它的回收有三种可能&#xff1a; YGC FGC MixedGC GC pause表示STW,Evacuation表示复制对象&#xff0c;…