go实现判断20000数据范围内哪些是素数(只能被1和它本身整除的数),采用多协程和管道实现
实现一个并发程序,用于寻找 20000 以内的所有素数。使用了 Goroutines 和 Channels 来分发和处理任务,并通过 WaitGroup(实现为 exitChan)来同步 Goroutines 的退出。
一.GO代码
package mainimport ("fmt""time"
)// 判断20000数据范围内哪些是素数(只能被1和它本身整除的数) 开启4个协程完成 采用管道同步通信 sync.WaitGroup
// WaitGroup 通常用于当只需要知道一组 Goroutines 何时结束,而不需要它们之间通信的场景
func main() {// 创建用于保存待检查数字的通道intChan := make(chan int, 1000)// 创建用于保存素数结果的通道primeChan := make(chan int, 2000)// 创建用于协调 Goroutines 退出的通道exitChan := make(chan bool, 4) // 协程数量并不是越多越快 根据CPU核数改变充分利用CPU性能// 开始时间 时间戳//startTime := time.Now().Unix()startTime := time.Now()// 开启一个 Goroutine 向 intChan 写入数据go putNum(intChan)// 开启 8 个 Goroutines 从 intChan 读取数据并判断是否为素数for i := 0; i < cap(exitChan); i++ {go primeNum(intChan, primeChan, exitChan)}// 开启一个匿名 Goroutine 等待所有 primeNum Goroutines 完成go func() {for i := 0; i < cap(exitChan); i++ {<-exitChan // 等待每个 primeNum Goroutine 的退出信号}// 结束时间useTime := time.Now().Sub(startTime)fmt.Println("-----------------所用时间:------------------------", useTime) // 所用时间: 3.1556msclose(primeChan) // 所有 primeNum Goroutines 完成后关闭 primeChan}()for i := 0; i < 10; i++ {go say(i)//time.Sleep(time.Second)}// 从 primeChan 中读取并打印素数结果for {//prime, ok := <-primeChan_, ok := <-primeChanif !ok {break // 如果 primeChan 被关闭,则退出循环}//fmt.Println("素数:", prime)}fmt.Println("主线程退出!!!!!!!!!!")
}// putNum 函数:向 intChan 中写入数字
func putNum(intChan chan int) {for i := 1; i <= 20000; i++ {intChan <- i // 将数字 1 到 20000 写入 intChan}close(intChan) // 写入完成后关闭 intChanfmt.Println("向intChan写入2000条数据完成")
}// primeNum 函数:从 intChan 中读取数字并判断是否为素数
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {for {num, ok := <-intChan // 从 intChan 中读取数据if !ok {fmt.Println("其中一个协程数据处理完毕~~~")break // 如果 intChan 被关闭,则退出循环}// 判断读取的数字是否为素数if isPrime(num) {primeChan <- num // 如果是素数,将其发送到 primeChan}}exitChan <- true // 发送退出信号到 exitChan
}// isPrime 函数:判断一个数是否为素数
/*
假设 n 不是素数,那么它可以表示为两个因子的乘积,即 n = a * b。
如果 a 和 b 都大于 sqrt(n)(n 的平方根),那么 a * b 将大于 n,这与 n = a * b 矛盾。
因此,如果 n 有超过1和它本身以外的因子,它必定至少有一个因子是小于或等于 sqrt(n) 的。
*/
func isPrime(n int) bool {if n == 1 {return false // 1 不是素数}for i := 2; i*i <= n; i++ { //如果一个数不是素数,则它必定有一个因子小于或等于它的平方根。if n%i == 0 {return false // 如果 n 能被除了 1 和它本身以外的数整除,则不是素数}}return true // 如果不能被任何数整除,则是素数
}func say(i int) {defer func() {if e := recover(); e != nil {fmt.Println("eeeeee:", e)}}()if i == 5 {panic("say-------------------i==5 err")}fmt.Printf("协程 %d 执行完毕\n", i)
}
二.管道只读和只写
在 Go 语言中,通道(Channel)是用于在不同的 Goroutines 之间传递数据的一种方式。通道可以是双向的,也可以是单向的。单向通道分为只读和只写通道。这种区分有助于提高程序的类型安全性和清晰性。
1.只读通道(Receive-Only Channel)
只读通道是一种只能从中接收数据的通道。在函数参数中使用只读通道可以确保该函数不会意外地向通道发送数据。
声明只读通道的方式是:<-chan DataType。
2.只写通道(Send-Only Channel)
只写通道是一种只能向其发送数据的通道。在函数参数中使用只写通道可以确保该函数不会意外地从通道读取数据。
声明只写通道的方式是:chan<- DataType。
3.示例
下面是一个简单的例子,展示了如何使用只读和只写通道:
goCopy codepackage mainimport ("fmt""time"
)// 该函数接收一个只写通道参数,只能向该通道发送数据
func sendOnly(ch chan<- int) {for i := 0; i < 5; i++ {ch <- i}close(ch) // 发送完数据后关闭通道
}// 该函数接收一个只读通道参数,只能从该通道读取数据
func receiveOnly(ch <-chan int) {for v := range ch {fmt.Println("Received:", v)}
}func main() {ch := make(chan int)go sendOnly(ch) // 启动一个 Goroutine 向通道发送数据receiveOnly(ch) // 在主 Goroutine 中从通道接收数据fmt.Println("Done")
}
在这个例子中:
sendOnly函数有一个只写通道参数,它向该通道发送一系列整数,然后关闭通道。receiveOnly函数有一个只读通道参数,它从该通道接收并打印数据,直到通道被关闭。- 在
main函数中,我们创建了一个双向通道ch,然后启动sendOnly函数在一个新的 Goroutine 中运行,并在主 Goroutine 中调用receiveOnly函数。

三.select的应用介绍
在 Go 语言中,select 语句是一种处理多个通道(Channel)的方式。它可以监听多个通道上的发送和接收操作,并且当任何一个通道准备就绪时,select 就会执行该操作。如果多个通道同时就绪,select 将随机选择一个执行。select 语句是非阻塞的,它可以与 Go 的并发特性结合,实现高效的任务处理和通信。
1.基本语法
select 语句的基本语法如下:
select {
case <-chan1:// 执行通道 chan1 上的接收操作
case chan2 <- value:// 向通道 chan2 发送值 value
default:// 如果以上都没有准备就绪,则执行默认操作
}
2.示例
-
启动多个协程,每个协程向各自的通道发送数据。
-
使用
select语句来接收不同协程的数据,同时监控超时情况和程序结束信号。package mainimport ("fmt""math/rand""time" )func sendData(ch chan<- int, id int) {for {// 模拟随机的发送间隔time.Sleep(time.Duration(rand.Intn(3)) * time.Second)ch <- id} }func main() {rand.Seed(time.Now().UnixNano())// 创建两个通道ch1 := make(chan int)ch2 := make(chan int)// 创建一个超时通道timeout := make(chan bool)// 创建一个结束信号的通道done := make(chan bool)// 启动协程发送数据go sendData(ch1, 1)go sendData(ch2, 2)// 启动一个协程来控制超时go func() {time.Sleep(5 * time.Second) // 设置超时时间为5秒timeout <- true}()// 使用 select 处理不同的情况for {select {case msg := <-ch1:fmt.Printf("Received from ch1: %d\n", msg)case msg := <-ch2:fmt.Printf("Received from ch2: %d\n", msg)case <-timeout:fmt.Println("Operation timed out!")done <- truereturncase <-done:fmt.Println("Program ended!")return}} }- 有两个数据发送协程,每个协程向其通道
ch1和ch2发送一个唯一的标识符。 - 设置了一个超时协程,如果在5秒内没有完成操作,则向
timeout通道发送一个信号。 - 在
main函数的select语句中,我们监听四种情况:从ch1接收数据、从ch2接收数据、超时和结束程序。 - 一旦超时发生,我们向
done通道发送一个信号并结束程序。
- 有两个数据发送协程,每个协程向其通道
四.recover
在 Go 中,协程(Goroutines)是轻量级的线程,用于并发执行任务。当一个协程因为 panic 而异常中断时,它不会影响其他协程的运行,但是如果 panic 没有被捕获(recover),它会导致整个程序崩溃。因此,在协程中合理使用 recover 是处理 panic 的一种有效方法。每个协程都应该独立地处理它们自己的 panic。这意味着你应该在每个可能产生 panic 的协程中使用 recover。recover 需要在 defer 函数中使用,因为只有在延迟函数中它才能捕获到协程的 panic。
1.示例
package mainimport ("fmt""time"
)func main() {// 启动多个协程for i := 0; i < 3; i++ {go safeGoroutine(i)}// 等待足够长的时间以确保协程执行time.Sleep(1 * time.Second)fmt.Println("主程序结束")
}func safeGoroutine(id int) {defer func() {if r := recover(); r != nil {fmt.Printf("协程 %d 捕获到 panic: %v\n", id, r)}}()// 这里是协程可能会触发 panic 的地方if id == 1 { // 假设只有 id 为 1 的协程会触发 panicpanic(fmt.Sprintf("协程 %d 发生 panic", id))}fmt.Printf("协程 %d 执行完毕\n", id)
}
main函数启动了 3 个协程。- 每个协程都调用了
safeGoroutine函数,在这个函数中,我们使用defer和recover来捕获并处理可能发生的 panic。 - 如果在协程中发生 panic,
recover会捕获到它,并允许协程优雅地处理 panic,而不是使整个程序崩溃。
2.位置
将 defer func() { ... }() 放在函数中的最上面是一种最佳实践。
- 确保覆盖整个函数: 将
defer放在函数开始处可以确保无论 panic 在函数的哪个部分发生,defer代码块都将被执行。这意味着,无论是由于哪个操作引发的 panic,都会被defer中的recover捕获和处理。 - 防止遗漏 panic: 如果将
defer放在函数中间或末尾,那么在defer之前的代码如果发生了 panic,recover将无法捕获到这个 panic,因为defer语句本身还没有被执行。
2.位置
将 defer func() { ... }() 放在函数中的最上面是一种最佳实践。
- 确保覆盖整个函数: 将
defer放在函数开始处可以确保无论 panic 在函数的哪个部分发生,defer代码块都将被执行。这意味着,无论是由于哪个操作引发的 panic,都会被defer中的recover捕获和处理。 - 防止遗漏 panic: 如果将
defer放在函数中间或末尾,那么在defer之前的代码如果发生了 panic,recover将无法捕获到这个 panic,因为defer语句本身还没有被执行。 - 逻辑清晰: 将
defer放在函数开头,可以让读代码的人立即知道这个函数有处理 panic 的逻辑,这使得代码的逻辑更清晰、更易于理解。
相关文章:
go实现判断20000数据范围内哪些是素数(只能被1和它本身整除的数),采用多协程和管道实现
实现一个并发程序,用于寻找 20000 以内的所有素数。使用了 Goroutines 和 Channels 来分发和处理任务,并通过 WaitGroup(实现为 exitChan)来同步 Goroutines 的退出。 一.GO代码 package mainimport ("fmt""time…...
GPT只是开始,Autonomous Agents即将到来
生成式AI虽然很早便已经引起了广泛关注,但直到ChatGPT的出现,许多公司的领导层才切身感受到了大语言模型(LLM)带来的深远影响。面临这种行业变革,诸多企业正争先恐后地加入到这场潮流中,但生成式AI的进步速…...
ubuntu source: not found
1、原因分析: shell 的解释器不是 bash,需把 shell 的解释器更改为 bash 2、ls -l /bin/sh 3、sudo dpkg-reconfigure dash 选择No 4、ls -l /bin/sh 5、reboot(此步必须持续,否则无效)...
Rancher部署k8s集群测试安装nginx(节点重新初始化方法,亲测)
目录 一、安装前准备工作计算机升级linux内核时间同步Hostname设置hosts设置关闭防火墙,selinux关闭swap安装docker 二、安装rancher部署rancher 三、安装k8s安装k8s集群易错点,重新初始化 四、安装kutectl五、测试安装nginx工作负载 一、安装前准备工作…...
SpringBoot结合thymeleaf的HTML页面不能跳转问题踩坑
问题描述:写了一个上传接口,controller不能跳转到thymeleaf的HTML页面“uploadsuccess”,试了好几个方法,都不起作用,后来发现是注解ResponseBody 的原因,把ResponseBody 去掉,问题解决,记录一下…...
Apache Zeppelin结合Apache Airflow使用1
Apache Zeppelin结合Apache Airflow使用1 文章目录 Apache Zeppelin结合Apache Airflow使用1前言一、安装Airflow二、使用步骤1.目标2.编写DAG2.加载、执行DAG 总结 前言 之前学了Zeppelin的使用,今天开始结合Airflow串任务。 Apache Airflow和Apache Zeppelin是两…...
分组循环A
模板 i 0 while(i<n){start iwhile( i<n && check(args) ) {i1} }1. LC 3011 判断一个数组是否可以变为有序 这题我比赛时用的并查集。看灵神视频学了个分组循环的做法。 对于每个分组,如果可以交换,则扩展分组的窗口,直至…...
《WebKit 技术内幕》学习之九(4): JavaScript引擎
4 实践——高效的JavaScript代码 4.1 编程方式 关于如何使用JavaScript语言来编写高效的代码,有很多铺天盖地的经验分享,以及很多特别好的建议,读者可以搜索相关的词条,就能获得一些你可能需要的结果。同时,本节希望…...
[SpringBoot2.6.13]FastJsonHttpMessageConverter不生效
文章目录 错误描述问题分析打印目前所有的消息处理器寻找适配版本消息解释器加载顺序 错误原因正确写法使用最新版本fastjson(2024-1-22)配置fastjson2消息转换器(保留系统原消息转换器)替换消息转换器配置fastjson2 错误描述 采用Bean的方式配置FastJsonHttpMessageConverter…...
(delphi11最新学习资料) Object Pascal 学习笔记---第3章第一节(简单语句与复合语句)
Object Pascal 学习笔记,Delphi 11 编程语言的完整介绍 作者: Marco Cantu 笔记:豆豆爸 3.1 简单语句与复合语句 编程指令通常称为语句。一个程序块可以由多个语句组成。有两种类型的语句,简单语句和复合语句。当语句不包含任何其他子语…...
Unity - 简单音频
“Test_04” AudioTest public class AudioTest : MonoBehaviour {// 声明音频// AudioClippublic AudioClip music;public AudioClip se;// 声明播放器组件private AudioSource player;void Start(){// 获取播放器组件player GetComponent<AudioSource>();// 赋值…...
SpringCloud中服务间通信(应用间通信)-亲测有效-源码下载-连载2
1、微服务概述 本案例主要解决微服务之间的相互调用问题 如果已经理解什么是微服务,可以直接跳到实战。 本案例采用springBoot3.1.7springCloud2022.0.4版本测试 本案例使用springboot2.7.x版本测试代码相同 1、微服务是分布式架构,那么为什么要需要…...
Axios取消请求:AbortController
AbortController AbortController() 构造函数创建了一个新的 AbortController 实例。MDN官网给出了一个利用AbortController取消下载视频的例子。 核心逻辑是:利用AbortController接口的只读属性signal标记fetch请求;然后在需要取消请求的时候࿰…...
【江科大】STM32:(超级详细)定时器输出比较
文章目录 输出比较单元特点 高级定时器:均有4个通道 PWM简介PWM(Pulse Width Modulation)脉冲宽度调制输出比较通道PWM基本结构基本定时器 参数计算捕获/比较通道的输出部分详细介绍如下: 舵机介绍硬件电路 直流电机介绍ÿ…...
Go 复合数据类型
1. 数组(array)(OK) 数组数组的概念数组是具有固定长度且拥有零个或多个相同数据类型元素的序列 i. 元素的数据类型相同 ii. 长度固定的序列 iii. 零个或多个元素的序列 与 slice 对比 由于数组的长度固定,所以在 G…...
Redis(01)——常用指令
基础指令 select 数字:切换到其他数据库flushdb:清空当前数据库flushall:清空所有数据库dbsize:查看数据库大小exists key1[key2 …]:判断当前的key是否存在keys *:查看所有的keyexpire key 时间ÿ…...
基本语法和 package 与 jar
3.基本语法 1.输入输出 // 导入 java.util 包中的 Scanner 类 import java.util.Scanner;// 定义名为 ScannerExample 的公共类 public class ScannerExample {// 主方法,程序的入口点public static void main(String[] args) {// 创建 Scanner 对象,用…...
本地读取Excel文件并进行数据压缩传递到服务器
在项目开发过程中,读取excel文件,可能存在几百或几百万条数据内容,那么对于大型文件来说,我们应该如何思考对于大型文件的读取操作以及性能的注意事项。 类库:Papa Parse - Powerful CSV Parser for JavaScript 第一步…...
【开源】基于JAVA的停车场收费系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 停车位模块2.2 车辆模块2.3 停车收费模块2.4 IC卡模块2.5 IC卡挂失模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 停车场表3.2.2 车辆表3.2.3 停车收费表3.2.4 IC 卡表3.2.5 IC 卡挂失表 四、系统实现五、核心代码…...
基于java+Springboot操作系统教学交流平台详细设计实现
基于javaSpringboot操作系统教学交流平台详细设计实现 🍅 作者主页 央顺技术团队 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 文末获取源码联系方式 📝 🍅 查看下方微信号获取联系方式 承接各种定制系统…...
Manus开源框架:高效探索与开发灵巧手抓取技能
1. 项目概述与核心价值最近在机器人抓取领域,一个名为“Manus Open Claw Skill Hunter and Developer”的项目引起了我的注意。这个项目由Simplio Labs开源,它不是一个具体的硬件爪子,也不是一个单一的算法,而是一个专门用于发现、…...
Git 进阶实战:如何优雅地从“被污染”的工作区中拯救代码
这是一篇为你整理的通用技术文档,旨在解决开发中常见的“Git 仓库被编译产物污染”及“提交异常”问题。 Git 进阶实战:如何优雅地从“被污染”的工作区中拯救代码 在 Android 系统开发或大型工程项目中,我们经常遇到一个头疼的问题:执行 git status 时,发现有几十甚至上…...
Equalizer APO:Windows系统音频均衡终极指南,免费打造专业级音效体验
Equalizer APO:Windows系统音频均衡终极指南,免费打造专业级音效体验 【免费下载链接】equalizerapo Equalizer APO mirror 项目地址: https://gitcode.com/gh_mirrors/eq/equalizerapo 想要彻底提升Windows电脑的音频质量吗?Equalize…...
U-Boot MMC DM驱动移植实战:从设备树配置到调试排错
1. 项目概述与核心价值最近在为一个基于i.MX6UL的工控板卡适配新的eMMC存储芯片时,又和U-Boot的MMC驱动打了一次交道。这让我想起,很多嵌入式开发者在进行板级移植或更换存储介质时,面对U-Boot中那套基于设备模型(Device Model, D…...
STM32 PVD中断防数据丢失实战:手把手教你配置2.9V阈值与紧急保存逻辑
STM32 PVD中断防数据丢失实战:手把手教你配置2.9V阈值与紧急保存逻辑 当嵌入式设备在野外采集数据或进行关键操作时,突然断电可能导致数月积累的传感器数据毁于一旦。我曾在一个农业物联网项目中亲历这种灾难——某次田间设备因电池接触不良断电…...
Emacs实时语法检查优化:flymake-cursor插件实现光标悬停提示
1. 项目概述:Emacs 实时语法检查的得力助手如果你是一个 Emacs 用户,并且主要用它来写代码,那么你一定对“实时语法检查”这个功能不陌生。在编写代码时,能够即时看到潜在的错误、拼写问题或者代码风格警告,这能极大地…...
深度学习立体匹配:从MC-CNN架构解析到工程实践优化
1. 项目概述:从传统到深度,立体匹配的范式革新在计算机视觉领域,立体匹配是一个经典且核心的问题,它的目标是从一对经过校正的左右图像中,为每个像素找到其在另一幅图像中的对应点,从而计算出场景的深度信息…...
ROS2 Galactic下源码编译TEB局部规划器:从依赖安装到成功运行Navigation2的保姆级避坑记录
ROS2 Galactic源码编译TEB局部规划器全流程实战指南 在机器人导航领域,TEB(Timed Elastic Band)局部规划器因其优秀的动态避障能力而备受青睐。然而当我们将目光转向ROS2 Galactic时,会发现官方仓库并未提供预编译的TEB功能包&…...
基于MCP协议构建AI与MongoDB数据交互的标准化桥梁
1. 项目概述:一个为AI应用注入数据库灵魂的MCP服务器如果你正在开发基于大语言模型(LLM)的AI应用,比如一个智能客服、一个文档分析助手,或者一个能帮你从海量数据中提炼洞察的智能体,你可能会遇到一个核心痛…...
现在不掌握NotebookLM航天科研工作流,你将错过下一轮国家重大专项申报窗口期——3大航天高校已启用的AI原生课题孵化模板首次解密
更多请点击: https://intelliparadigm.com 第一章:NotebookLM航天科学研究 NotebookLM 是 Google 推出的基于 AI 的研究协作者工具,专为处理长文档、技术报告与多源文献而设计。在航天科学研究中,其语义理解能力与引用溯源机制可…...
