GO 语言协程知识点学习笔记

news2024/11/23 6:23:33

GO 语言协程知识点学习笔记 是个人从互联网上学习整理的笔记。因个人技艺不精,如有纰漏,还请斧正。

协程?

协程并不是 GO 语言特有的机制,像 Lua、Ruby、Python、Kotlin、C/C++ 也都有协程的支持。区别在于有些是从语言层面支持,有的通过插件类库支持。Go 语言的协程是原生语言层面支持的。

协程和进程和线程的对比
进程
  • 进程是系统资源分配的最小单位。进程的创建和销毁都是系统资源级别。操作起来代价昂贵。程是抢占式调度,分为三个状态:等待态、就绪态、运行态。进程之间是相互隔离的,拥有各自的系统资源,更加安全。因此存在进程之间通讯不方便的问题
  • 进程是线程的载体容器,多个线程除了共享进程的资源还拥有自己的一少部分独立的资源。因此相比进程而言线程更加清凉,进程内的多个线程间的通信比进程容易,但也同样带来了同步和互斥的问题和线程安全的问题(多线程编程的常见问题),尽管多线程编程仍然是当前服务端编程的主流,线程也是 CPU 调度的最小单位,多线程运行时存在线程切换的问题,其状态的转移如图:

协程
  • 协程在有的资料中成为微线程或者用户态轻量级线程,协程调度不需要系统内核参与而是完全由用户态程序来决定,因此协程对于系统而言是无感知的。协程由用户态控制就不存在抢占式调度。抢占式调度会强制让 CPU 控制权切换到其它进线程,多个协程进行协作实调度,协程自己主动把控制权转让出去后,其它协程 才能被执行到,这样就避免了系统切换开销提高了 CPU 的使用效率。
抢占式调度和协作式调度的简单对比图:

Go 协程

启用一个 Go 协程非常简单,只需要在函数前加上关键字 go,就可以轻易的启用一个新的 Go 协程并发运行。

package main

import "fmt"

func main() {
	go hello()
	fmt.Println("main function")
}

func hello() {
	fmt.Println("Hello goroutine")
}

go hello() 此处启动了一个协程,因此hello() 函数会与 main() 函数并发执行。main() 函数会运行在一个特有的 Go 协程上,它被称为 Go 的主协程(Main Goroutine)。

运行结果大概率如下:

main function

Process finished with exit code 0

为什么只输出了 main function ?我们的 Hello goroutine 为什么没有输出呢?

  • 当启动一个新的协程时,协程的调用会立即返回,与函数不同,程序控制不回去等待 Go 的协程执行完毕,在本例中即 hello() 函数,因此程序控制会立即返回到代码的下一行,即 fmt.Println("main function") ,忽略该协程的任何返回值
  • 协程的存在与否取决于 main() 主协程是否存活,如果主协程被销毁(即程序终止了),那么其余用 go 关键字启用的协程,在本例中为 hello() 协程也被销毁了。这就导致了主协程已经结束,但没有完全等待 hello() 协程的执行完毕,就被销毁了。因此出现了以上情况。

看到这里,你应该知道为什么运行结果是大概率如下,因为主协程和协程的执行顺序是不一致的,在一定概率下,即主协程正好等到了协程的执行完毕,会输出 Hello goroutine

让我们来修复下这个问题吧!

package main

import (
	"fmt"
	"time"
)

func main() {
	go hello()
	time.Sleep(1 * time.Second)
	fmt.Println("main function")
}

func hello() {
	fmt.Println("Hello goroutine")
}

可以看到,我们利用 time 包中的 Sleep 方法,去休眠主协程,让其等待 1 秒钟,等待 hello() 协程执行完毕。那么最终输出结果如下:

Hello goroutine
main function

Process finished with exit code 0

注意:在实际业务中,利用协程休眠的方式去控制协程的执行顺序是不可取的,因为这样的预测永远无法 100% 如你所愿。如果需要控制协程的同步,我们会在下面讲到 channel 即管道或信道。

启用多个 Go 协程

为了更好的理解 Go 协程,我们再编写一个程序,启动多个 Go 协程

package main

import (
	"fmt"
	"time"
)

func main() {
	go hello()
	go anotherHello()
	time.Sleep(1 * time.Second)
	fmt.Println("main function")
}

func hello() {
	for i := 0; i <= 3; i++ {
		time.Sleep(250 * time.Millisecond)
		fmt.Println("Hello goroutine")
	}
}

func anotherHello() {
	for i := 0; i <= 3; i++ {
		time.Sleep(400 * time.Millisecond)
		fmt.Println("Hello another goroutine")
	}
}
  • hello() 协程会在第一次输出 Hello goroutine 后,休眠该协程 250 毫秒,再次输出 Hello goroutine ,直到 i > 3
  • anotherHello() 协程会在第一次输出 Hello another goroutine 后,休眠该协程 400 毫秒,再次输出 Hello another goroutine ,直到 i > 3
  • 最终主协程会在该两个协程执行完毕后,输出 main function

是不是很简单?Go 语言屏蔽了多线程的复杂实现,只需要一个 go 关键字即可轻而易举的创建一个新的协程。

什么是 Channel 管道:

Channel 是 Go 协程之间通信的通信管道,如同管道中的谁会从一端流到另一端,通过使用管道,就可以实现数据从管道的一个端口发送,在另外一个端口接收。

管道声明(无缓冲管道)

声明管道就像声明一个切片一样简单:

package main

func main() {
	c := make(chan int)
	// 或者
	var c2 chan int
}
  • chan T 表示该管道只能运送 T 类型的数据,在本例中为 int 类型数据
通过管道进行发送与接收
a := make(chan int)
getDataFromChannelA := <-a // 读取管道 a 发送的内容
a <- 1                     // 向管道 a 发送 1 

怎么样?是不是很简单。在一开始可能会觉得管道操作符比较难记,其实我们只需要记住简单的方法即可

  • a <- 1 向管道 a 发送 1,箭头指向管道,即代表发送
  • <- a 从管道 a 读取数据,箭头不指向管道,即代表从管道接收
发送与接收默认是阻塞的

发送于接收默认是阻塞的,这是什么意思呢?当把数据发送至管道时,程序控制会产生阻塞,直到有另外一个协程(可以是主协程)来进行接收,直到其它协程接收了来自管道的数据,反之亦然。否则程序才能继续运行,否则将一直阻塞。

利用该特性,我们可以很容易实现一个不用加锁的同步的协程通信的管道。那么我们如果实现一个发送与接受不阻塞的管道呢?其实只需要将管道增加缓冲即可。

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

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

相关文章

【2024最新】基于springboot+vue的xxxx平台lw+ppt

作者&#xff1a;计算机搬砖家 开发技术&#xff1a;SpringBoot、php、Python、小程序、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;Java精选实战项…

linux源码安装slurm以及mung和openssl

一、源码安装munge 1、编译安装munge &#xff08;1&#xff09;下载munge地址&#xff1a;https://github.com/dun/munge/releases &#xff08;2&#xff09;解压编译安装&#xff1a; 1 2 3 4 5 6 7 8 创建/data目录 复制文件munge-0.5.15.tar.xz 到/data目录下 tar -Jx…

模型 知识诅咒

系列文章 分享 模型&#xff0c;了解更多&#x1f449; 模型_思维模型目录。知者难悟无知惑。 1 知识诅咒案例 1.1 会议室的误解 李经理是一家科技公司的产品经理&#xff0c;他负责领导一个新产品的开发项目。项目团队由不同背景和经验的成员组成&#xff0c;包括新入职的员…

php 生成随机数

记录:随机数抽奖 要求:每次生成3个 1 - 10 之间可重复(或不可重复)的随机数,10次为一轮,每轮要求数字5出现6次、数字4出现3次、…。 提炼需求: 1,可设置最小数、最大数、每次抽奖生成随机数的个数、是否允许重复 2,可设置每轮指定数字的出现次数 3,可设置每轮的抽奖…

(32)噪声信号的时域分析:均值、方差、与功率

文章目录 前言一、生成噪声信号并画图二、计算信号的均值、方差、与功率三、结果分析 前言 本文对叠加了高斯白噪声的一段整周期余弦信号进行时域分析&#xff0c;使用MATLAB进行信号生成&#xff0c;并计算其均值、方差、与功率。最后给出对计算结果的分析&#xff0c;阐明均…

组装首页:其他组件html、css移入JS中再引入首页

组装首页 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>组装首页</title><style>* …

Java-Exception与RuntimeException

&#x1f496;简介 &#x1f4d6;Exception Exception 类是所有非致命性异常的基类。这些异常通常是由于编程逻辑问题或外部因素&#xff08;如文件不存在、网络连接失败等&#xff09;导致的&#xff0c;可以通过适当的编程手段来恢复或处理。Exception 可以进一步分为两大类…

分享一些常用的数据库性能监测工具

以下是一些常用的数据库性能监测工具&#xff1a; 一、MySQL MySQL Enterprise Monitor&#xff1a; 由 MySQL 官方推出&#xff0c;提供全面的数据库性能监控、诊断和优化功能。可以监控数据库的各种指标&#xff0c;如查询性能、连接数、缓存命中率等&#xff0c;并提供警报…

目标检测——YOLO11算法解读

作者&#xff1a;Ultralytics公司 代码&#xff1a;https://github.com/ultralytics/ultralytics YOLO系列算法解读&#xff1a; YOLOv1通俗易懂版解读、SSD算法解读、YOLOv2算法解读、YOLOv3算法解读、YOLOv4算法解读、YOLOv5算法解读、YOLOR算法解读、YOLOX算法解读、YOLOv6算…

(南京观海微电子)——HKC_5.0_QP050AS01-1_MIPI_LVDS_RGB原理及代码介绍

1. MIPI 2. LVDS 3. TTL 4.代码 //初始化代码 Generic_Short_Write_1P(0xee,0x01); // ENTER PAGE1 Generic_Short_Write_1P(0xea,0x07); Generic_Short_Write_1P(0xeb,0x12); Generic_Short_Write_1P(0x0a,0x76); // vcom //Generic_Sh…

兆易创新Cortex-M7 GD32H459适配OpenHarmony轻量系统适配教程

笔者利用国庆假期的时间适配了一款Cortex-M7 的国产厂商兆易创新GD32H459&#xff0c;开源地址&#xff1a;https://gitee.com/GD32H759_OpenHarmony OpenHarmony 4.1r 轻量系统移植到GD32H759文档 1.学习本文档的意义 1.学习如何移植OpenHarmony轻量系统4.1r到GD32 m7内核G…

[论文期刊|稳定检索]2024年信号处理与光学工程国际会议(SPOE 2024 )

2024年信号处理与光学工程国际会议 2024 International Conference on Signal Processing and Optical Engineering 【1】大会信息 会议名称&#xff1a;2024年信号处理与光学工程国际会议 会议简称&#xff1a;SPOE 2024 大会时间&#xff1a;请查看官网 大会地点&#xf…

【JS试题】对象键排序问题的神仙试题

前言 题目如下&#xff1a; const obj { a: 0 } obj[1] 0 obj[obj.a] obj.a const values Object.values(obj) obj[values[1]] obj.a console.log(obj);在此之前需要先了解 JS 对象键的排序问题&#xff0c;JS会对对象的属性进行处理&#xff0c;把所有 Number类型 和 数…

AWS S3迁移到阿里云OSS实践

本教程讲解如何将AWS S3中的数据迁移到阿里云对象存储OSS。 如果您需要将AWS S3中的数据通过专线迁移到阿里云对象存储OSS&#xff0c;请参见使用代理迁移。 概述 阿里云在线迁移服务是阿里云提供的存储产品数据通道。使用在线迁移服务&#xff0c;您可以将第三方数据轻松迁…

GaussDB主备版 8 工具学习

1 客户端工具 1.1 gsql 1.1.1 类似plsql 1.1.2 默认只支持从服务器本机连接&#xff0c;如果需要连接到远端的数据库&#xff0c;必须在服务端进行配置 1.1.3 gsql --help 1.1.4 命令参考-https://doc.hcs.huawei.com/db/zh-cn/gaussdb/24.1.30/tg/gaussdb-38-0007.html 1.1.…

Golang | Leetcode Golang题解之第474题一和零

题目&#xff1a; 题解&#xff1a; func findMaxForm(strs []string, m, n int) int {dp : make([][]int, m1)for i : range dp {dp[i] make([]int, n1)}for _, s : range strs {zeros : strings.Count(s, "0")ones : len(s) - zerosfor j : m; j > zeros; j--…

【网络基础知识】网络通信概述与TCPIP、UDP协议

网络基础知识 介绍网络基础知识&#xff0c;譬如网络通信概述、OSI 七层模型、IP 地址、TCP/IP 协议族、TCP 和 UDP 协议等等&#xff0c; 旨在以引导入门、了解为主&#xff0c;其中并不会深入、详细地介绍这些内容&#xff1b; Linux网络编程入门移步&#xff1a;【Linux网络…

使用CSS和HTML实现3D图片环绕效果

使用CSS和HTML实现3D图片环绕效果 在本篇博客中&#xff0c;将介绍如何使用HTML和CSS实现一个3D图片环绕效果。这个效果不仅具有视觉吸引力&#xff0c;而且具有高度的互动性&#xff0c;鼠标悬停时动画会暂停。接下来将一步步讲解这个效果的实现过程。 1. 效果 2. 页面结构与…

Python人脸识别技术进阶篇

在上一篇文章中&#xff0c;我们介绍了如何使用Python进行基本的人脸识别。本文将深入探讨人脸识别技术的高级应用&#xff0c;包括如何优化识别性能、处理复杂环境下的识别挑战以及如何利用深度学习模型来提高识别准确性等话题。 人脸识别的原理回顾 人脸识别流程 人脸识别…