协程求素数的代码实现 | 学习笔记

简介: 快速学习协程求素数的代码实现

开发者学堂课程【Go语言核心编程 - 面向对象、文件、单元测试、反射、TCP编程协程求素数的代码实现】学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/626/detail/9760


协程求素数的代码实现

 

内容介绍

一、新建文件

二、新建三个管道

 

一、新建文件

打开 chapter16,新建一个文件夹,命名为 goroutineapply03,这是我们的03案例,接下来再新建一个文件夹,新建一个文件 main.go  

组代码

packge main

import (

“fmt”

“time”//需要一个 time 包

)

讲解:开始组函数

Func main(){

}

根据刚才的思路,我们要先建三个管道出来


二、新建三个管道

//根据我们刚才的思路,我们应该新建三个管道出来

image.png

intchan := make(chan int ,1000)  

讲解:类型是放整数的,大小可以随意,放1000或者100都可以,因为只要有读,就可以

primeChan := make(chan int,2000)//放入结果

//标识退出的管道

讲解:创建第二个,这个就要大一点,但其实弄小一点,做完能够提出来放到一个 main 里面也可以。这里将放大一点的数字。有两个思路后,还有第三个就是退出的一个管道。

exitchan := make(chan bool, 4) // 4个

讲解:这是我们需要一个 bool 形式的,也可以弄其他的,只要适和就行。但这个是明确的,就是4个,因为目前开的协程就是4个。如果要放其他数值,那个这方法就不适用,需要另想其它的办法,一招不能走遍天下,要多动脑筋想想,可以把 bool 换成 main,

//开启一个协程,向 intChan 放入 1-8000个数

讲解:这个我们可以用函数去写,但我习惯单写一个函数

//向 intChan 放入 1-8000个数

func putNum(intChan chan int)  {  

讲解:需要先把管道传过来,管道的类型是引用

for i := 1; i <= 8000; i++{ //循环

intChan<- i

}

讲解:因为是1-8000,所以就放一个 i,然后退出。要注意,在没有关闭之前,线程就已经开始工作了,并不是关闭才能读,关闭是读到这个位置的时候就会退出,仅有这个作用而已。所以他在往里面放的时候就已经开始工作了,

//关闭 intChan

Close(intChan)

讲解:关闭就意味着结束了,所以说只要做一件事情。

}

//开启4个协程,向 intChan 取出数据,并判断是否为素数(如果是,就放入到 primeChan)

Func primenum(intchan chan int, primechan chan int,exitchan chan bool){

讲解:首先你要清楚这个要传进来是从哪取的,这个是必须的。第二个,你要将 primechan 这个管道传进来。做完后,如果你的 prime 取不到的话,你要往退出的管道里写,所以你还需要传一个东西出来,即 exitchan chan,bool类型,因为它需要三个管道,这种情况下,需要 for 循环

//使用for循环

//Var num int

Var flag bool //

讲解:假定它是一个素数,但是否是我们并不知道,如果在 for 循环中有一个进来了,就说明它不是素数,所以直接将 flag 设为 false,如果不是素数,就直接退出。就会有这样一种情况,除了他自身还有别的数字能整除,就说明他不是素数,就直接退出。在整个判断 num 的过程中,它一直没有进到这个里面来,说明我们的假设是对的,如果是对的呢,就将这个数放入到 primechan 中,因为这个管道是安全的,所以放一个就好。

For{

Num := <-intchan

Flag = true  //假设是素数

//判断num是不是素数

for i := 2; I < num;i++ {

if num % 1 == 0 {//说明该 num 不是素数

flag = false

break

}

}

If flag{

//将这个数放入到 primechan

讲解:取个 num,这个 num 可以定到外面去会比较好一点,这里不需要定义,只需要在管道取个东西,取出来后,你就该判断它是不是一个素数了。素数是除了自己其它都不能整除的数字。先在前面定一个 flag。

Primechan<-num

讲解:放入一个 num,放入之后,可以看到它做完一个数又会再去取,会在 intchan 取不到的时候退出,所以这样做我们可能会更好。

Num ,OK= <-intchan

If  !ok {//intchan 取不到

Break

}

讲解:发现取不到值后,也应该退出。就可能有两种可能退出来。For循 环的 break 是 for 循环的退出,上面的break 是指退出外循环,就管道取不出来了,两个概念不一样。如果我取到了,那就去判断是不是,如果发现他不是,那就退出再把它加进去,需要不停的这样做。当他最后跳出整个 for 循环的时候,就意味着当前这个协程完成了。

}

Fmt. Println(“有一个 primenum 协程因为取不到数据,退出”)

//这里我们还不能关闭

//向 exitchan 写入 true

exitChan<-true

讲解:这里有一个关键,就是这里能退出来吗?退出的原因是取不出来,但是因为有的人能取出来,而有的人却不能,所以这个时候我们不能把他关闭,你要做这样一件事,向退出的管道 exitchan 写入 true,完成后离开。

Func main(){

intchan := make(chan int ,1000)  

primeChan := make(chan int,2000)//放入结果

//标识退出的管道

exitchan := make(chan bool, 4) // 4个

//开启一个协程,向 intChan 放入 1-8000个数

Go putnum(intchan)

讲解:就此时此刻,这个协程就已经开始工作了。接下去来我们要写 putnumber 这个协程。开启四个携程

//开启4个协程,向 intChan 取出数据,并判断是否为素数(如果是,就放入到 primeChan)

讲解:我们要取4个协程放在4个 CPU 上,结果和退出的标识放入,这四个已经是在同时工作了,这个就做完了。

For i :=0; I < 4; i ++{

go putNum(intChan, primeChan, exitChan)

讲解:现在把所有的都协调好了,要不停的写,不停的读,但是如果没有 for 这部分的处理,这些都白干,因为如果不做处理的话,主线程就消失了,所以我们现在要看的就是这个部分。我们现在结果都还没有得出,他现在还处于打开的状态,所以我们要等待他把所有的事都做完,同时还要解决把它关掉的问题,所以这是一个关键点。

//这里我们主线程,进行处理

讲解:我们关键的问题是从 exitChan 管道里取出四个结构,证明大家都完成,判断标准是从退出管道里取出4个 t,说明任务是真的完成,甚至说明 primechan 这个管道里也是全部正确,因为4个协程里都有 true,所以需要 for 循环。

//直接

go func(){

}

For i :=0; i < 4; i ++{

讲解:要从退出管道里取出4个,结果没有重要性,管道没有用,只需要取出4个东西,至于这个地方是什么,不重要;如果取不到4个,那就需要等待。如果结果是4个,那我们也就结束了。

<- exitChan

}

//当我们从 exitChan 取出4个结果,就可以放心的关闭 primeChan

讲解:因为我们真正需要的东西是 primeChan

close(primeChan)

}()

讲解:这一部分跟主线上发生关系也特别不好,所以直接使用 go func()函数让它跑起来,作为一个协程让他去做,让他帮我们看看有没有。

//遍历我们的 primeChan,把结果取出

for {

讲解:取值从primeChan中取,因为有一个协程帮我们关,所以他总有一个时间点取不到,所以我们就直接 break,把结果打出来,

res,ok := <- primeChan

if !ok{

break

}

//将结果输出

Fmt . printf(“素数=%d\n”,res)

}

fmt . println(“main线程退出”)

讲解:在这里提示一句话。最后在资源管理器中检查代码,发现重复代码的就可以不用,例如  //Var num int。因我们选的数字8000太大,可带入较小的数字入80,验证一下,至少应该打出四句话,运行,可以看到下图,这个结果是正确的,有一个素数。但为什么一下就退出来了呢?是因为存的速度低于读的速度而造成的,这个的问题不是很大。

image.png

可以让他的速度变得慢一点,添加 time sleep(time millisecond*10),使用毫秒,可以在手册-资料-api-time 中查到,同时需要一个 time 包,看看效果,

image.png

可以看到速度变慢,结果也是正确的,但是有一个重点,素数结果的打印是在哪里打印的呢?

for {

res,ok := <- primeChan

if !ok{

break

}

//将结果输出

Fmt . printf(“素数=%d\n”,res)

}

可以看到是在这里打印的,这就很奇怪,打印的时候是在后面打印的,但是统计的时候,Fmt. Println(“有一个primenum 协程因为取不到数据,退出”)这个是在前面的,为什么会这样呢,因为它是同步的,开始执行的时候就是同步的,没有顺序而言,不存在等待的问题,就是一边取,一边就统计。数据量可以调大,调为8000

image.png

可以看到输出的是正确的。

相关文章
|
4月前
|
前端开发 编译器 程序员
协程问题之为什么 C++20 的协程代码比其他语言的协程 demo 长很多如何解决
协程问题之为什么 C++20 的协程代码比其他语言的协程 demo 长很多如何解决
|
6月前
|
程序员 开发者 Python
深入浅出Python协程:提升代码效率的秘诀
【2月更文挑战第12天】 在当今追求高效编程的时代,Python协程成为了开发者提升代码执行效率的重要工具。本文将以通俗易懂的方式,深入探讨Python协程的原理、使用方法及其在实际开发中的应用场景。通过对比传统同步编程和异步编程的差异,我们将揭示协程如何在不牺牲代码可读性的前提下,显著提高程序的运行效率。文章旨在为Python开发者提供一份全面、实用的协程学习指南,帮助他们在实际项目中更好地利用这一强大的特性。
55 2
|
6月前
|
调度 开发者 Python
深入浅出Python协程:提升并发编程效率深入浅出Python协程:提高代码效率的秘诀
在现代软件开发中,提高程序的执行效率和响应速度是一个永恒的追求。本文将带你深入理解Python协程(Coroutine),一种轻量级的并发编程解决方案。与传统的多线程和多进程相比,协程提供了更高效的并发性能,尤其适用于IO密集型任务。我们将通过简单的示例,探讨协程的工作原理、如何在Python中使用协程以及它们如何帮助我们达到更高的编程效率。 在现代软件开发中,异步编程已成为提高应用性能和响应速度的关键技术之一。Python,作为一门广泛使用的编程语言,其协程(Coroutine)功能为开发者提供了强大的异步编程工具。本文将通过深入浅出的方式,探讨Python协程的基本概念、工作原理以及如
|
6月前
|
程序员 调度 开发者
深入浅出Python协程:提升代码效率的艺术
在当今快速发展的软件开发领域,提高代码执行效率成为了每个开发者的追求。本文将带您深入理解Python协程(Coroutine)的概念、工作原理及其在异步编程中的应用,通过对比传统的同步编程方式,展示协程如何优雅地处理I/O密集型任务,从而显著提升程序的运行效率。文章结合实例讲解如何在Python中使用协程,以及如何通过asyncio库来实现高效的异步编程模式,旨在为读者提供一个清晰、易于理解的协程学习路径。
72 1
|
6月前
|
API 调度 开发者
深入理解Python协程:提升代码效率的关键
本文旨在深入探讨Python中协程的概念、原理及其在实际开发中的应用。与常规的技术文章不同,我们将通过对比传统的线程和进程方式,揭示协程如何在保持低开销的同时提高程序的并发性能。文章首先简要介绍协程的基础知识,随后深入讨论Python中协程的实现机制,包括asyncio库的使用,最后通过实例演示协程在网络编程中的应用,展示其在异步IO操作中如何显著提升代码执行效率。
|
6月前
|
C++
C/C++协程学习笔记
C/C++协程学习笔记
|
6月前
|
存储 前端开发 rax
协程学习笔记
协程学习笔记
54 0
|
6月前
|
存储 前端开发 rax
协程学习笔记 NtyCo/libgo
协程学习笔记 NtyCo/libgo
93 0
|
安全 调度 数据库
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(下)
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(下)
67 0
|
Java Go Android开发
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(上)
Kotlin 学习笔记(五)—— 协程的基础知识,面试官的最爱了~(上)
89 0