【Go】十七、进程、线程、协程

文章目录

  • 1、进程、线程
  • 2、协程
  • 3、主死从随
  • 4、启动多个协程
  • 5、使用WaitGroup控制协程退出
  • 6、多协程操作同一个数据
  • 7、互斥锁
  • 8、读写锁
  • 9、defer+recover优化多协程

1、进程、线程

  • 进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域

在这里插入图片描述

  • 一个进程下面有多个线程,干着不同的活儿

在这里插入图片描述

进程与线程,好比打开360,同时进行木马查杀和电脑清理,360是一个进程,后面两个则是两个线程

在这里插入图片描述

补充,关于并行和并发:

  • 并发:多线程同时/交替操作同一资源类
  • 并行:多线程同时操作多个资源类

示意图:
在这里插入图片描述

2、协程

  • 协程是一种用户态的轻量级线程
  • 又称微线程、纤程
  • 是一种单线程下的并发
  • 协程中只有一个线程在执行(协程的本质是个单线程)

在这里插入图片描述

在一个单独的线程中,出现IO操作时,此时可控制单线程下的多个任务,在另一个任务IO阻塞时,将其寄存器上下文和栈保存到某个地方,去切到另一个任务继续计算。如此,就保证了线程最大程度的处于就绪状态,执行效率变高。

协程的引入,给CPU一种:该线程好像是一直在计算,io比较少的错觉,从而会更多的将cpu的执行权限分配给我们的线程

线程是CPU控制的,而协程是程序自身控制的,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级

感觉在线程的基础上再细分,还是因为后面计算机在硬件上发展快了,如此再做切换,可以更加提升效率。

package main
import(
        "fmt"
        "strconv"
        "time"
)
func test(){
        for i := 1;i <= 10;i++ {
                fmt.Println("hello golang + " + strconv.Itoa(i))
                //阻塞一秒:
                time.Sleep(time.Second)
        }
}
func main(){//主线程
        go test() //开启一个协程
        for i := 1;i <= 10;i++ {
                fmt.Println("hello 9527 + " + strconv.Itoa(i))
                //阻塞一秒:
                time.Sleep(time.Second)
        }
}

如上,主线程中,开启一个协程,协程每1秒输出hello golang,主线程每一秒输出一次hello 9527,主线程和协程在同时执行,且属于同一个线程(主线程)。运行:

在这里插入图片描述
在这里插入图片描述

3、主死从随

即:

  • 主线程执行结束退出了,则即使其下的协程没有执行完,也要跟着陪葬
  • 当然协程如果提前在主线程之前结束,那就正常自己结束就好
package main
import(
        "fmt"
        "strconv"
        "time"
)
func test(){
        for i := 1;i <= 1000;i++ {
                fmt.Println("hello golang + " + strconv.Itoa(i))
                //阻塞一秒:
                time.Sleep(time.Second * 1)
        }
}
func main(){//主线程
        go test() //开启一个协程
        for i := 1;i <= 10;i++ {
                fmt.Println("hello msb + " + strconv.Itoa(i))
                //阻塞一秒:
                time.Sleep(time.Second * 1)
        }
}

在这里插入图片描述

4、启动多个协程

package main
import(
        "fmt"
        "time"
)
func main(){
        //匿名函数+外部变量 = 闭包
        for i := 1;i <= 5;i++ {
                //启动一个协程
                //使用匿名函数,直接调用匿名函数
                go func(n int){
                        fmt.Println(n)
                }(i)
        }
        time.Sleep(time.Second * 2)
}

5、使用WaitGroup控制协程退出

思想类似Java的计数器那些JUC辅助类,用来解决主线程在子协程结束后自动结束,即阻塞线程,等等所有协程执行完。核心方法:

//协程开始的时候加1操作
func (wg*WaitGroup) Add(delta int)

//协程执行完后减一
func(wg *WaitGroup) Done()

//WaitGroup为0前,阻塞线程
func(wg *WaitGroup) Wait()

示例:

package main
import(
        "fmt"
        "sync"
)
var wg sync.WaitGroup //只定义无需赋值
func main(){
        //启动五个协程
        for i := 1 ;i <= 5;i++ {
                wg.Add(1) //协程开始的时候加1操作
                go func(n int){
                        fmt.Println(n)
                        wg.Done()  //协程执行完成减1
                }(i)
        }
        //主线程一直在阻塞,什么时候wg减为0了,就停止
        wg.Wait()
}

当然也可用defer关键字去减一

package main
import(
        "fmt"
        "sync"
)
var wg sync.WaitGroup 
func main(){
        for i := 1 ;i <= 5;i++ {
                wg.Add(1) 
                go func(n int){
                        defer wg.Done()	//!!!这里
                        fmt.Println(n)		
                }(i)
        }
        wg.Wait()
}

可以最开始在知道协程次数的情况下先Add操作

package main
import(
        "fmt"
        "sync"
)
var wg sync.WaitGroup 
func main(){
        wg.Add(5)		//这里!!!
        for i := 1 ;i <= 5;i++ {
                go func(n int){
                        defer wg.Done()
                        fmt.Println(n)		
                }(i)
        }
        wg.Wait()
}

注意Add的个数和协程的个数要一致。

6、多协程操作同一个数据

开一个协程去做一万次+1,再开一个协程去做一万次-1

package main
import(
        "fmt"
        "sync"
)
//定义一个变量:
var totalNum int
var wg sync.WaitGroup //只定义无需赋值
func add(){
        defer wg.Done()
        for i := 0 ;i < 100000;i++{
                totalNum = totalNum + 1
        }
}

func sub(){
        defer wg.Done()
        for i := 0 ;i < 100000;i++{
                totalNum = totalNum - 1
        }
}

func main(){
        wg.Add(2)
        //启动协程
        go add()
        go sub()
        wg.Wait()
        fmt.Println(totalNum)
}

运行的结果始终不为0:

在这里插入图片描述

多协程操作同一个数据的问题:按以下1.2.3.4.5.6的步骤,就发现做了一次+1,一次-1,结果为-1

在这里插入图片描述

修复这个问题,让一个协程执行逻辑的时候,另一个协程不执行 ⇒ 互斥锁

7、互斥锁

引入sync包:

//加入互斥锁:
var lock sync.Mutex
package main
import(
        "fmt"
        "sync"
)
//定义一个变量:
var totalNum int
var wg sync.WaitGroup //只定义无需赋值
//加入互斥锁:
var lock sync.Mutex
func add(){
        defer wg.Done()
        for i := 0 ;i < 100000;i++{
                //加锁
                lock.Lock()
                totalNum = totalNum + 1
                //解锁:
                lock.Unlock()
        }
}
func sub(){
        defer wg.Done()
        for i := 0 ;i < 100000;i++{
                //加锁
                lock.Lock()
                totalNum = totalNum - 1
                //解锁:
                lock.Unlock()
        }
}
func main(){
        wg.Add(2)
        //启动协程
        go add()
        go sub()
        wg.Wait()
        fmt.Println(totalNum)
}

8、读写锁

互斥锁在读多写少的场景不适合,性能低下,采用读写互斥,但读读共享的读写锁。

//加入读写锁:
var lock sync.RWMutex
lock.RLock()//读锁
lock.RUnlock()

示例:

package main
import(
        "fmt"
        "sync"
        "time"
)
var wg sync.WaitGroup //只定义无需赋值
//加入读写锁:
var lock sync.RWMutex
func read(){
        defer wg.Done()
        lock.RLock()//如果只是读数据,那么这个锁不产生影响,但是读写同时发生的时候,就会有影响
        fmt.Println("开始读取数据")
        time.Sleep(time.Second)
        fmt.Println("读取数据成功")
        lock.RUnlock()
}
func write(){
        defer wg.Done()
        lock.Lock()
        fmt.Println("开始修改数据")
        time.Sleep(time.Second * 10)
        fmt.Println("修改数据成功")
        lock.Unlock()
}
func main(){
        wg.Add(6)
        //启动协程 ---> 场合:读多写少
        for i := 0;i < 5;i++ {
                go read()
        }
        go write()
        wg.Wait()
}

运行发现:写的时候不能读,但读的时候可以共享读:

在这里插入图片描述

9、defer+recover优化多协程

多协程工作,一个协程出现panic,整个程序崩溃。引入defer+recover,让协程即使出现错误,也不影响主线程和其他协程的执行:

ackage main
import(
        "fmt"
        "time"
)
//输出数字:
func printNum(){
        for i := 1;i <= 10;i++{
                fmt.Println(i)
        }
}
//做除法操作:
func devide(){
        defer func(){
                err := recover()
                if err != nil{
                        fmt.Println("devide()出现错误:",err)
                }
        }()
        num1 := 10
        num2 := 0
        result := num1 / num2
        fmt.Println(result)
}
func main(){
        //启动两个协程:
        go printNum()
        go devide()
        time.Sleep(time.Second * 5)
}

运行:

在这里插入图片描述

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

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

相关文章

Emacs之解除comment-region绑定C-c C-c快捷键(一百三十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

​做一个个人博客第一步该怎么做?零基础就找一个现成的模板学一学呗

做一个个人博客第一步该怎么做&#xff1f; 好多零基础的同学们不知道怎么迈出第一步。 那么&#xff0c;就找一个现成的模板学一学呗&#xff0c;毕竟我们是高贵的Ctrl c v 工程师。 但是这样也有个问题&#xff0c;那就是&#xff0c;那些模板都&#xff0c;太&#xff01;…

pygame--坦克大战(一)

项目搭建 本游戏主要分为两个对象,分别是我方坦克和敌方坦克。用户可以通过控制我方的坦克来摧毁敌方的坦克保护自己的“家”,把所有的敌方坦克消灭完达到胜利。敌方的坦克在初始的时候是默认5个的(这可以自己设置),当然,如果我方坦克被敌方坦克的子弹打中,游戏结束。从…

C++的字节对齐

什么是字节对齐 参考什么是字节对齐&#xff0c;为什么要对齐? 现代计算机中&#xff0c;内存空间按照字节划分&#xff0c;理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问&#xff0c;这就需要各种类型数据按照一定的规…

【网站项目】课堂教学效果实时评价系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

2024 Python 最新趋势

Python 于 2023 年庆祝其诞生 31 周年。它将成功地完成其在竞技场上的三十年&#xff0c;并成功地与许多其他重要的编程语言进行激烈的竞争。因此很明显&#xff0c;2023 年 Python 对于软件开发人员来说非常重要。 Python 是一种通用、高级、解释性编程语言。如今&#xff0c…

leet hot 100-13 最大子数组和

53. 最大子数组和 原题链接思路代码 原题链接 leet hot 100-10 53. 最大子数组和 思路 生成一个数字来记录last 表示前面数字全部之和与0取最大值 如果大于0 就加上如果不大于0 就不管 从当前位置从新开始遍历计算 时间复杂度O(n) 空间复杂度(1) 代码 class Solution {…

C++——异常机制

目录 一&#xff0c;背景 1.1 C语言处理错误的方式 1.2 C异常概念 二&#xff0c;异常的使用 2.1 异常的简单使用 2.2 异常的匹配原则 2.3 异常抛对象 2.4 异常的重新抛出 2.5 异常安全 三&#xff0c;自定义异常体系 四&#xff0c;异常优缺点 4.1 优点 4.2 缺点 …

女大三抱金砖?看完这篇起诉状就明白:猜疑乃婚姻之大敌

女大三抱金砖&#xff1f;看完这篇起诉状就明白&#xff1a;猜疑乃婚姻之大敌 阿勇与阿芳&#xff0c;一对年过四十的夫妻&#xff0c;且有一对已成年的儿女&#xff0c;如今走到了婚姻的尽头。原告阿勇指控双方感情早已破裂&#xff0c;受父母包办婚姻影响&#xff0c;两人经常…

XL5300(ToF)传感器芯片产品介绍,可最大 4m 的精确距离测量

XL5300 是一款单模块封装 ToF 传感器&#xff0c;采用了 SPAD、TDC 和直方图技术&#xff0c;可实现最大 4000 mm 的精确距离测量&#xff0c;片内集成了单光子雪崩二极管&#xff08;SPAD&#xff09;接收阵列以及VCSEL激光发射器。该传感器可对物体进行精确的距离测量而不受物…

蓝桥杯物联网竞赛_STM32L071_15_ADC/脉冲模块

ADC模块用的是RP1不用多说了&#xff0c;主要是脉冲模块&#xff0c;这个模块没考过 这个脉冲模块放出脉冲&#xff0c;主要能用TIM捕获到这个脉冲的高电平持续时间即可 CubMx配置&#xff1a; 脉冲模块的引脚与PB0相连&#xff0c;所以用PB0读取上升沿记的数和下降沿记的数&am…

视频剪辑软件哪个好?2024会声会影怎么样呢?

随着科技的不断发展&#xff0c;视频制作已经不再是专业人士的专属领域&#xff0c;越来越多的人开始使用各种视频制作软件来记录生活、创作内容。其中&#xff0c;会声会影是被广泛使用的一款视频制作软件&#xff0c;其旗舰版更是备受关注。 视频剪辑软件哪个好&#xff1f;…

信创采购风向标政策!乡镇及以上单位采购台式机/便携式需符合信创要求!

继2023年12月26日财政部会同工业和信息化部研究正式发布7项基础软硬件政府采购需求标准后&#xff0c;信创采购又迎风向标政策&#xff01;从7项基础软硬件政府采购需求标准&#xff0c;看信创采购风向标&#xff01; 2024年3月11日&#xff0c;中央政府采购网发布《关于更新中…

LeetCode-19. 删除链表的倒数第 N 个结点【链表 双指针】

LeetCode-19. 删除链表的倒数第 N 个结点【链表 双指针】 题目描述&#xff1a;解题思路一&#xff1a;双指针解题思路二&#xff1a;优化解题思路三&#xff1a;0 题目描述&#xff1a; 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。…

今日头条signature参数js逆向(爬虫)

今日头条是ajax动态加载 话不多说&#xff0c;直接上代码 windowglobal;window.location{"ancestorOrigins": {},"href": "https://www.toutiao.com/","origin": "https://www.toutiao.com","protocol": "…

python基础——模块【模块的介绍,模块的导入,自定义模块,*和__all__,__name__和__main__】

&#x1f4dd;前言&#xff1a; 这篇文章主要讲解一下python基础中的关于模块的导入&#xff1a; 1&#xff0c;模块的介绍 2&#xff0c;模块的导入方式 3&#xff0c;自定义模块 &#x1f3ac;个人简介&#xff1a;努力学习ing &#x1f4cb;个人专栏&#xff1a;C语言入门基…

Mediapipe框架(二)人脸检测

Mediapipe框架(二)人脸检测 MediaPipe 是一款由 Google Research 开发并开源的多媒体机器学习模型应用框架。谷歌的一系列重要产品&#xff0c;如Google Lens、ARCore、Google Home等都已深度整合了 MediaPipe。 MediaPipe目前支持的解决方案(Solution)及支持的平台如下图所示…

得物面试:10wqps高并发,如何防止重复下单?

尼恩说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格&#xff0c;遇到很多很重要的面试题&#xff1a; 10wqps高并发&#xff0c;如何防止重复提交/支付订单&…

基于springboot+vue+微信小程序的医院预约挂号系统(前后端分离)(含参考论文)

基于springbootvue微信小程序的医院预约挂号系统(前后端分离)(含参考论文) 前言 本系统适用于毕业设计、课程设计或者学习等&#xff0c;适合选题&#xff1a;医院预约挂号、微信小程序、前后端分离等。系统采用springbootvue整合开发&#xff0c;前端框架主要使用了element-…

半山腰总是挤的,你得去山顶看看

如果你去爬山&#xff0c;你会发现&#xff0c;半山腰的人总是最多的&#xff0c;越往上走&#xff0c;人越少&#xff0c;而最好的风景你只能到山顶去看。所以如果你想要欣赏到最好的风景&#xff0c;往往付出的努力也最多。爬山不能走捷径&#xff0c;只能你一步一个脚印走上…