Golang使用Channel

2023-09-16 21:46:09

1.使用协程

package main

//使用协程
import (
	"fmt"
	"strconv"
	"time"
)

func test() {
	for i := 1; i <= 10; i++ {
		fmt.Println("test() hello world" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

// 主线程和test协程同时运行
func main() {
	go test() //开启了一个协程
	for i := 1; i <= 10; i++ {
		//strconv.Itoa函数:将整型转换为字符串
		fmt.Println("main() hello Golang" + strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
main() hello Golang1
test() hello world1
main() hello Golang2
test() hello world2
test() hello world3
main() hello Golang3
main() hello Golang4
test() hello world4
test() hello world5
main() hello Golang5
main() hello Golang6
test() hello world6
test() hello world7
main() hello Golang7
main() hello Golang8
test() hello world8
test() hello world9
main() hello Golang9
main() hello Golang10
test() hello world10

2.使用20个协程计算1到20各个数的阶乘,把结果放到map中

package main

import (
	"fmt"
	"sync"
	"time"
)

//计算1-20的各个数的阶乘,并且把各个数的阶乘放入到map中
//最后显示出来,要求使用goroutine完成

//思路:1.编写一个函数,计算各个数的阶乘,并放入到map中
//2.我们启动的协程多个,统计的结果放入到map中
//3.map应该做出一个全局变量

var (
	myMap = make(map[int]int, 20)
	//lock是一个全局的互斥锁
	//Mutex:互斥
	lock sync.Mutex
)

func test(n int) {
	res := 1
	for i := 1; i <= n; i++ {
		res *= i
	}
	//把结果放入到map中
	//concurrent map writes
	//加锁
	lock.Lock()
	myMap[n] = res
	lock.Unlock()
}

func main() {

	//开启多个协程完成这个任务
	for i := 1; i <= 20; i++ {
		go test(i)
	}

	//让主线程休眠,因为主线程执行完后,协程就会中止
	time.Sleep(time.Second * 5)

	//输出结果,遍历这个结果
	//lock.Lock()
	for i, v := range myMap {
		fmt.Printf("map[%d]=%d\n", i, v)
	}
	//lock.Unlock()
}
map[3]=6
map[7]=5040
map[10]=3628800
map[11]=39916800
map[19]=121645100408832000 
map[20]=2432902008176640000
map[13]=6227020800
map[9]=362880
map[16]=20922789888000     
map[4]=24
map[5]=120
map[6]=720
map[8]=40320
map[15]=1307674368000
map[18]=6402373705728000
map[17]=355687428096000
map[14]=87178291200
map[1]=1
map[2]=2
map[12]=479001600

3.Channel的使用

(1).Channel中只能存放指定的数据类型

(2).Channel数据放满后,就不能放入了

(3).如果从Channel中取出数据后,可以继续放入

(4).在没有使用协程的情况下,如果Channel数据取完了,再取,就会报dead lock

package main

import "fmt"

/**
Channel使用注意事项
1.Channel中只能存放指定的数据类型
2.Channel数据放满后,就不能放入了
3.如果从Channel中取出数据后,可以继续放入
4.在没有使用协程的情况下,如果Channel数据取完了,再取,就会报dead lock
*/
//演示管道需求
//channel相当于queue

func main() {
	//1.创建一个存放3个int类型的管道
	var intChan chan int
	//capacity=3
	intChan = make(chan int, 3)

	//向管道写入数据
	intChan <- 10
	num := 211
	intChan <- num

	//4.看看管道的长度和容量
	fmt.Printf("channel len=%v cap=%v \n", len(intChan), cap(intChan))

	//5.从管道中读取数据
	var num2 int
	num2 = <-intChan
	fmt.Println("num2=", num2)
	fmt.Printf("channel len=%v cap=%v \n", len(intChan), cap(intChan))
}
channel len=2 cap=3
num2= 10
channel len=1 cap=3

4.遍历管道

package main

import "fmt"

// 1.关闭管道,管道不能被写入数据,但是可以取出数据
func test1() {
	intChan := make(chan int, 3)
	intChan <- 100
	intChan <- 200
	close(intChan)
	intChan <- 300
	fmt.Println("Hello")

}

// 遍历管道
func test2() {
	intChan := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intChan <- i * 2
	}
	//这个方法的前提是Channel要关闭,否则出现deadlock
	close(intChan)
	for v := range intChan {
		fmt.Println("v=", v)
	}
}

func main() {
	test2()
}

5.goroutine和channel协同工作案例

package main

import (
	"fmt"
	"time"
)

/*
*
goroutine和channel协同工作案例
1.开启一个writeData协程,向管道intChan输入50个整数
2.开启一个readData协程,从管道intChan中读取writeData写入数据
3.writeData和readData操作的都是同一条管道
4.主线程需要等待writeData和readData协程都完成工作才退出
*/
func writeData(intChan chan int) {
	for i := 1; i <= 50; i++ {
		intChan <- i
		time.Sleep(time.Second)
		fmt.Println("writeData=", i)
	}
	close(intChan)
}
func readData(intChan chan int, exitChan chan bool) {
	for {
		v, ok := <-intChan
		if !ok {
			break
		}
		time.Sleep(time.Second)
		fmt.Println("readData=", v)
	}
	//读完数据后
	exitChan <- true
	close(exitChan)
}
func main() {
	intChan := make(chan int, 50)
	exitChan := make(chan bool, 1)
	go writeData(intChan)
	go readData(intChan, exitChan)

	//time.Sleep(time.Second * 10)
	for {
		_, ok := <-exitChan
		if ok {
			break
		}
	}
}
writeData= 1
readData= 1
readData= 2
writeData= 2
readData= 3
writeData= 3
readData= 4
writeData= 4
readData= 5
writeData= 5
readData= 6
writeData= 6
readData= 7
writeData= 7
readData= 8
writeData= 8
readData= 9
writeData= 9
readData= 10
writeData= 10
readData= 11
writeData= 11
readData= 12
writeData= 12
readData= 13
writeData= 13
readData= 14
writeData= 14
readData= 15
writeData= 15
readData= 16
writeData= 16
readData= 17
writeData= 17
readData= 18
writeData= 18
readData= 19
writeData= 19
readData= 20
writeData= 20
readData= 21
writeData= 21
readData= 22
writeData= 22
readData= 23
writeData= 23
readData= 24
writeData= 24
readData= 25
writeData= 25
readData= 26
writeData= 26
readData= 27
writeData= 27
readData= 28
writeData= 28
readData= 29
writeData= 29
readData= 30
writeData= 30
readData= 31
writeData= 31
readData= 32
writeData= 32
readData= 33
writeData= 33
readData= 34
writeData= 34
readData= 35
writeData= 35
readData= 36
writeData= 36
readData= 37
writeData= 37
readData= 38
writeData= 38
readData= 39
writeData= 39
readData= 40
writeData= 40
readData= 41
writeData= 41
readData= 42
writeData= 42
readData= 43
writeData= 43
readData= 44
writeData= 44
readData= 45
writeData= 45
readData= 46
writeData= 46
readData= 47
writeData= 47
readData= 48
writeData= 48
readData= 49
writeData= 49
readData= 50
writeData= 50

6.协程求素数

package main

import (
	"fmt"
	"time"
)

//协程求素数
//要求统计1-200的数字中,哪些是素数?

// 使用并行的方式,将统计素数的任务分配给多个(4个)goroutine去完成
func putNum(intChan chan int) {
	for i := 1; i <= 200; i++ {
		intChan <- i
	}
	//关闭
	close(intChan)
}

// 从intChan取出数据,判断是否为素数,如果是,就放入到primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {

	for {
		num, ok := <-intChan
		if !ok {
			break
		}
		flag := true
		for i := 2; i < num; i++ {
			if num%i == 0 {
				flag = false
				break
			}
		}
		if flag {
			//放入素数管道
			primeChan <- num
		}
	}
	fmt.Println("有一个primeNum协程因为取不到数据,退出")
	//这里我们还不能关闭primeChan
	//向exitChan写入true
	exitChan <- true
}

func main() {
	intChan := make(chan int, 1000)
	//放入结果
	primeChan := make(chan int, 10000)

	//标识退出的管道
	exitChan := make(chan bool, 4)

	start := time.Now().Unix()
	//开启一个协程,向intChan放入1-8000个数
	go putNum(intChan)

	//开启4个协程向intChan取出数据并判断是否为素数
	//如果是素数直接放入primeChan
	for i := 0; i < 4; i++ {
		go primeNum(intChan, primeChan, exitChan)
	}

	go func() {
		//这里主线程要进行处理
		for i := 0; i < 4; i++ {
			<-exitChan
		}
		//从exitChan取出了4个结果,可以放心的关闭primeChan
		end := time.Now().Unix()
		fmt.Println("使用协程耗时=", end-start)
		close(primeChan)
	}()

	//遍历primeNum,把结果取出
	for {
		res, ok := <-primeChan
		if !ok {
			break
		}
		fmt.Printf("素数=%d\n", res)
	}
	fmt.Println("main()退出")
}
素数=1
素数=2                                
有一个primeNum协程因为取不到数据,退出
有一个primeNum协程因为取不到数据,退出
有一个primeNum协程因为取不到数据,退出
素数=3                                
有一个primeNum协程因为取不到数据,退出
使用协程耗时= 0                       
素数=5                                
素数=7                                
素数=11                               
素数=13                               
素数=17                               
素数=19                               
素数=23                               
素数=29                               
素数=31                               
素数=37                               
素数=41                               
素数=43                               
素数=47                               
素数=53                               
素数=59                               
素数=61                               
素数=67                               
素数=71
素数=73
素数=79
素数=83
素数=89
素数=97
素数=101
素数=103
素数=107
素数=109
素数=113
素数=127
素数=131
素数=137
素数=139
素数=149
素数=151
素数=157
素数=163
素数=167
素数=173
素数=179
素数=181
素数=191
素数=193
素数=197
素数=199
main()退出

7.使用select解决管道阻塞问题

package main

import (
	"fmt"
)

//使用select解决管道阻塞问题

func main() {

	intChan := make(chan int, 10)
	for i := 0; i < 10; i++ {
		intChan <- i
	}

	stringChan := make(chan string, 5)
	for i := 0; i < 5; i++ {
		stringChan <- "hello" + fmt.Sprintf("%d", 5)
	}

	//传统的方法遍历管道,如果不关闭会阻塞,会遭遇deadlock
	//有可能不好确定什么时候关闭管道
	//使用select解决

	for {
		select {
		//注意:如果intChan一直没有关闭,不会一直阻塞而deadlock
		//会自动的到下一个case
		case v := <-intChan:
			fmt.Println("从intChan读取数据 ", v)
			//time.Sleep(time.Second)
		case v := <-stringChan:
			fmt.Println("从stringChan读取数据 ", v)
			//time.Sleep(time.Second)
		default:
			fmt.Println("都读取不到了,不玩了")
			return
		}
	}
}

 8.goroutine中使用recover可以解决协程中出现panic

package main

import (
	"fmt"
	"time"
)

// goroutine中使用recover可以解决协程中出现panic,导致程序崩溃问题
func sayHello() {
	for i := 0; i < 10; i++ {
		time.Sleep(time.Second)
		fmt.Println("Hello world")
	}

}

func test() {
	//这里我们使用defer+recover
	defer func() {
		//捕获test抛出的panic
		//捕获了以后,这个协程发生错误不影响其他协程
		if err := recover(); err != nil {
			fmt.Println("test() 发生错误", err)
		}
	}()
	
	//定义了一个map,让它发生错误
	var myMap map[int]string
	myMap[0] = "Golang" //error
}
func main() {
	go sayHello()

	//panic: assignment to entry in nil map
	//报错引起整个程序崩溃
	go test()
	for i := 0; i < 10; i++ {
		fmt.Println("main() ok=", i)
		time.Sleep(time.Second)
	}
}

 

更多推荐

OpenCV实战(32)——使用SVM和定向梯度直方图执行目标检测

OpenCV实战(32)——使用SVM和定向梯度直方图执行目标检测0.前言1.HOG图像特征2.交通标志分类2.1SVM模型2.2SVM原理3.HOG可视化4.人物检测5.完整代码小结系列链接0.前言本节中,我们将介绍机器学习方法支持向量机(SupportVectorMachine,SVM),它可以根据训练数据得到准确

Moonbeam新版开发者网站上线,助力开发者Keep BUIDLing!

系统性学习Web3开发知识,很少有课程能满足理论学习+实践指导+长期扶持等多重需求。更重要的是,拥有翻译准确的中文版本!降低开发者进入Web3的学习和使用门槛,是Moonbeam团队坚持的“MassiveAdoption(大规模采用)”的必备要素。对开发者而言,重点是“学以致用”,及时准确地获取业内各类有效开发方案,及

2023 年最新 Docker 容器技术基础详细教程(更新中)

Docker基本概述Docker是一个开源的应用容器引擎,它让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。Docker官网:https://www.docker.comDocke

MAMMOTH: BUILDING MATH GENERALIST MODELS THROUGH HYBRID INSTRUCTION TUNING

PapernameMAMMOTH:BUILDINGMATHGENERALISTMODELSTHROUGHHYBRIDINSTRUCTIONTUNINGPaperReadingNotePaperURL:https://arxiv.org/pdf/2309.05653.pdfProjectURL:https://tiger

Zabbix预处理和数据开源节流

一、简介在监控环境中构建高频率的监控时,从设计选择的角度需要考虑如何减少性能影响,数据存储空间的保留时长等,Zabbix现成的功能解决能否解决这些问题,是值得探索的。本文中讨论什么时候应该使用预处理,什么时候适合使用“不保存历史记录”选项,谈及这两种方法的优缺点。二、数据节流及预处理数据节流是高频监测的首选方法。使用数

多模态大模型应用大观 | AIGC赋能医疗

浩渺宇宙中,生命与文明经历了亿万年的沉淀与演变,这是人类集体智慧逐步觉醒的过程,人们正在渐渐掌握加速前行的翅膀。从古老的蒸汽机到现代的电力,再跨越到计算机与互联网的时代,每一次人类文明的跃进,都离不开开拓者的勇敢探索。在不断地开拓进取中那些关键技术得以应用与普及,如同神之巨手颠覆着各行各业,推动着社会向前飞跃。2023

cmake应用:集成gtest进行单元测试

编写代码有bug是很正常的,通过编写完备的单元测试,可以及时发现问题,并且在后续的代码改进中持续观测是否引入了新的bug。对于追求质量的程序员,为自己的代码编写全面的单元测试是必备的基础技能,在编写单元测试的时候也能复盘自己的代码设计,是提高代码质量极为有效的手段。在本系列前序的文章中已经介绍了CMake很多内容,本文

3. MongoDB高级进阶

3.MongoDB高级进阶3.1.MongoDB的复制集3.1.1.复制集及原理MongoDB复制集的主要意义在于实现服务高可用复制集的现实依赖于两个方面的功能:数据写入时将数据迅速复制到另一个独立节点上在接受写入的节点发生故障时自动选举出一个新的替代节点复制集在实现高可用的同时,还有以下作用:数据分发:将数据从一个区

【MySQL进阶】SQL性能分析

一、SQL性能分析1.SQL执行频率MySQL客户端连接成功后,通过show[session|global]status命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次:--session是查看当前会话;--global是查询全局数据;SHO

六、变量与常量

变量与常量1.变量与常量1.1标识符和关键字1.1.1.标识符1.1.2.关键字1.2.声明变量1.3.声明常量1.4.变量的有效范围1.4.1.成员变量1.4.2.局部变量1.5.训练11.6.训练2——————————————————————————————————————————————————1.变量与常量在程

数据仓库性能测试方法论与工具集

目录文章目录目录数据仓库v.s.传统数据库数据仓库性能测试案例性能指标测试方案测试场景测试数据集测试用例性能指标测试脚本工具基准环境准备硬件环境软件环境测试操作步骤Cloudwave执行步骤导入数据集TestCase1.执行13条标准SQL测试语句TestCase2.执行多表联合join拓展SQL1测试语句TestCa

热文推荐