【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 := 10num2 := 0result := num1 / num2fmt.Println(result)
}
func main(){//启动两个协程:go printNum()go devide()time.Sleep(time.Second * 5)
}

运行:

在这里插入图片描述

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

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

相关文章

集合的学习

为什么要有集合&#xff1a;集合会自动扩容 集合不能存基本数据类型&#xff08;基本数据类型是存放真实的值&#xff0c;而引用数据类型是存放一个地址&#xff0c;这个地址存放在栈区&#xff0c;地址所指向的内容存放在堆区&#xff09; 数组和集合的对比&#xff1a; 集…

Flutter 开发学习笔记(3):第三方UI库的引入

文章目录 前言初始化程序Icon导入如何导入 Toast消息提示框引入简单封装简单使用 Charts图表导入新建pages文件夹存放page简单代码实现效果 总结 前言 Flutter已经发布了有10年了&#xff0c;生态也算比较完善了。用于安卓程序开发应该是非常的方便。我们这里就接入一些简单的…

golang语言系列:Web框架+路由 之 Gin

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是golang语言学习系列&#xff0c;本篇对Gin框架的基本使用方法进行学习 1.Gin框架是什么 Gin 是一个 Go (Golang) 编写的轻量级 http web 框架&#xff0c;运行速度非常快&#xff0c;如果你是性能和高效的追求者…

【JavaEE】_Spring MVC项目上传文件

目录 1. 文件上传具体实现 2. 保存文件 1. 文件上传具体实现 .java文件内容如下&#xff1a; package com.example.demo.controller;import com.example.demo.Person; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.Multip…

day4|gin的中间件和路由分组

中间件其实是一个方法&#xff0c; 在.use就可以调用中间件函数 r : gin.Default()v1 : r.Group("v1")//v1 : r.Group("v1").Use()v1.GET("test", func(c *gin.Context) {fmt.Println("get into the test")c.JSON(200, gin.H{"…

特征融合篇 | YOLOv8改进之将Neck网络更换为GFPN(附2种改进方法)

前言:Hello大家好,我是小哥谈。GFPN(Global Feature Pyramid Network)是一种用于目标检测的神经网络架构,它是在Faster R-CNN的基础上进行改进的,旨在提高目标检测的性能和效果。其核心思想是引入全局特征金字塔,通过多尺度的特征融合来提取更丰富的语义信息。具体来说,…

FPGA + 图像处理 (二) RGB转YUV色域、转灰度图及仿真

前言 具体关于色域的知识就不细说了&#xff0c;简单来讲YUV中Y通道可以理解为就是图像的灰度图&#xff0c;因此&#xff0c;将RGB转化为YUV是求彩色图的灰度直方图、进行二值化操作等的基础。 HDMI时序生成模块 这里先介绍一下仿真时用于生成HDMI时序&#xff0c;用这个时…

自贡市第一人民医院:超融合与 SKS 承载 HIS 等核心业务应用,加速国产化与云原生转型

自贡市第一人民医院始建于 1908 年&#xff0c;现已发展成为集医疗、科研、教学、预防、公共卫生应急处置为一体的三级甲等综合公立医院。医院建有“全国综合医院中医药工作示范单位”等 8 个国家级基地&#xff0c;建成高级卒中中心、胸痛中心等 6 个国家级中心。医院日门诊量…

【Docker】搭建便捷的Docker容器管理工具 - dockerCopilot

【Docker】搭建便捷的Docker容器管理工具 - dockerCopilot 前言 本教程基于绿联的NAS设备DX4600 Pro的docker功能进行搭建。前面有介绍过OneKey&#xff0c;而dockerCopilot便是OneKey的升级版&#xff0c;作者对其进行了重新命名&#xff0c;并且对界和功能都进行了全面的优…

负载均衡集群

一、集群的基本原理 集群&#xff1a;数据内容是一致的&#xff0c;集群可以被替代 分布式&#xff1a;各司其职&#xff0c;每台服务器存储自己独有的数据&#xff0c;对外作为单点被访问是访问整体的数据&#xff1b; 分布式是不能被替代的&#xff1b;分布式分为MFS、GFS、…

结构体内存对齐和位段(重点)!!!

乐观学习&#xff0c;乐观生活&#xff0c;才能不断前进啊&#xff01;&#xff01;&#xff01; 我的主页&#xff1a;optimistic_chen 我的专栏&#xff1a;c语言 点击主页&#xff1a;optimistic_chen和专栏&#xff1a;c语言&#xff0c; 创作不易&#xff0c;大佬们点赞鼓…

数据结构栈和堆列

目录 栈&#xff1a; 栈的概念&#xff1a; 栈的实现&#xff1a; 栈接口的实现&#xff1a; 1.初始化栈&#xff1a; 2.入栈&#xff1a; 3.出栈&#xff1a; 4. 获取栈顶元素&#xff1a; 5.获取栈中有效数据的个数&#xff1a; 6.检测栈是否为空&#xff0c;如果为…

Java代码示例:演示多态特性及子类方法重写(day17)

java代码里面体现多态的特点&#xff1a; 第一步创建一个父类father&#xff0c; 然后创建子类subclasses&#xff0c; 最后创建一个DemoMulti, 上面的父类特有的方法不是私有的&#xff0c;因此子类能够继承。 新建一个父类方法Father 创建子类subclasses 在下面的代码中…

LabVIEW深度学习

目录 一、配置环境1.1、显卡选择1.2、下载显卡驱动1.3、下载并安装Anaconda1.4、配置Anaconda软件包下载服务器1.5、配置虚拟环境tf_gpu1.6、安装vscode1.7、安装tensorflow1.8、下载安装Git1.9、安装TensorFlow Object Detection API框架1.10、安装依赖的python软件包1.11、配…

Python 简单使用 RabbitMQ

一、安装 pip install pika 二、推送消息到队列中 执行pythone方法 import pika import time# 用户名和密码 user_info pika.PlainCredentials(admin,admin)# 连接服务器上的rabbitMQ服务 connection pika.BlockingConnection(pika.ConnectionParameters(127.0.0.1, 5672,…

在开源的基础上构建 AI 需要一种全新的应用程序安全方法

人工智能已经从科幻小说中涌现出来&#xff0c;进入了我们的日常生活。 在开源软件&#xff08;OSS&#xff09;模型的支持下&#xff0c;人工智能革命正在加速。这些模型是专为开发 AI 而制作的复杂开源代码包&#xff0c;使组织能够高效、大规模地部署 AI 模型。 虽然大多数…

StreamingT2V文本生成视频多模态大模型,即将开源!

1、前言 Picsart人工智能研究所、德克萨斯大学和SHI实验室的研究人员联合推出了StreamingT2V视频模型。通过文本就能直接生成2分钟、1分钟等不同时间&#xff0c;动作一致、连贯、没有卡顿的高质量视频。 虽然StreamingT2V在视频质量、多元化等还无法与Sora媲美&#xff0c;但…

【C++第二阶段】文件操作

以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 文件操作文件写入流程简单的demo写操作 文件读流程二进制写文件二进制读文件 文件操作 文件写入流程 写文件包括以下几个步骤 1.包含头文件 2.创建流对象 3.打开文件&#xff0…

大数据学习第十二天(hadoop概念)

1、服务器之间数据文件传递 1&#xff09;服务器之间传递数据&#xff0c;依赖ssh协议 2&#xff09;http协议是web网站之间的通讯协议&#xff0c;用户可已通过http网址访问到对应网站数据 3&#xff09;ssh协议是服务器之间&#xff0c;或windos和服务器之间传递的数据的协议…

IP SSL的应用与安装

IP SSL&#xff0c;即互联网协议安全套接字层&#xff0c;它是一种为网络通信提供安全及数据完整性的安全协议。在网络传输过程中&#xff0c;IP SSL可以对数据进行加密&#xff0c;这样即便数据在传输途中被截取&#xff0c;没有相应的解密密钥也无法解读内容。这一过程如同将…