go语言学习——9

news2024/11/25 2:26:17

文章目录

  • goroutine
    • 概念
    • goroutine调度模型
  • channel
    • channel介绍
    • 定义/声明channel
    • channel的关闭
    • channel遍历
    • channel其他细节

goroutine

前言:统计1~90000000数字中,哪些是素数?

  • 使用循环,很慢
  • 使用并发或者并行的方式,将任务分配给多个goroutine去完成,就会使用到goroutine

概念

进程和线程

  • 进程是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位
  • 线程是进程的一个执行实例,是程序执行的最小单元,是比进程更小的能独立运行的基本单位
  • 一个进程可以创建核销多个线程,同一个进程中的多个线程可以并发执行
  • 一个程序至少有一个进程,一个进程至少有一个线程
    在这里插入图片描述

并发和并行

  • 多线程程序在单核上运行,就是并发
  • 多线程程序在多核上运行,就是并行
    在这里插入图片描述

GO协程和GO主线程

  • go主线程(直接称为线程/也可理解为进程):一个go线程上,可以起多个协程,可以理解为协程是轻量级的线程[编译器做优化]

  • go协程的特点:
  • 有独立的栈空间
  • 共享程序堆空间
  • 调度由用户控制
  • 协程是轻量级的线程

在这里插入图片描述

package main

import (
	"fmt"
	"strconv"
	"time"
)

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

func main()  {
	go test() //开启一个协程

	for i:=1;i<=5;i++{
		fmt.Println("main() hello,golang! "+strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

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

  • 主线程是一个物理线程,直接作用在cpu,是重量级的,非常耗费cpu资源
  • 协程从主线程开启,是轻量级的线程,是逻辑态,对资源消耗相对较小
  • golang的协程机制可以轻松开启上万个协程,其他编程语言的并发机制一般基于线程,开启过多线程,资源消耗大,这就凸显Golang在并发上的优势

goroutine调度模型

MPG模式
在这里插入图片描述MPG模式运行状态1
在这里插入图片描述MPG模式运行状态2
在这里插入图片描述

channel

前言:计算1~200各个数的阶乘,并且把各个数的阶乘放到map中。显示出来。用goroutine完成

  • 用goroutine完成,效率高,会出现并发/并行安全问题
  • 不同goroutine如何通信?

尝试的解决方案:

package main

import (
	"fmt"
	"time"
)

var (
	myMap=make(map[int]int,10)
)

//计算阶乘
func test(n int){
	res:=1
	for i:=1;i<=n;i++{
		res*=i
	}

	myMap[n]=res
}

func main()  {
	for i:=1;i<=200;i++{
		go test(i)
	}

	time.Sleep(time.Second*10)

	for i,v:=range myMap{
		fmt.Printf("map[%d]=%d\n",i,v)
	}
}

在这里插入图片描述运行结果:
在这里插入图片描述
不同goroutine如何通信
1、全局变量的互斥锁
2、使用管道channel来解决

使用全局变量加锁同步改进程序

未对全局变量m加锁,会出现资源争夺问题,代码报错,提示concurrent map writes

解决方案:加入互斥锁

tips:阶乘很大,结果可能越界,可改为sum+=unint64(i)

改进后代码:

package main

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

var (
	myMap=make(map[int]int,10)
//	声明一个全局互斥锁
//lock是一个全局变量互斥锁
//sync是包:synchornized同步
//Mutex:互斥
	lock sync.Mutex
)

//计算阶乘
func test(n int){
	res:=1
	for i:=1;i<=n;i++{
		res*=i
	}

	lock.Lock()
	myMap[n]=res
	lock.Unlock()
}

func main()  {
	for i:=1;i<=200;i++{
		go test(i)
	}

	time.Sleep(time.Second*10)

	lock.Lock()
	for i,v:=range myMap{
		fmt.Printf("map[%d]=%d\n",i,v)
	}
	lock.Unlock()
}

运行结果:
在这里插入图片描述
为什么需要channel?

  • 使用全局变量加锁同步来解决goroutine通讯,不利于多个协程对全局变量的读写操作
  • 主线程等待所有goroutine全部完成的时间很难确定
  • 主线程休眠时间长,会加长等待时间,等待时间短,goroutine可能处于工作状态,会随着主线程的退出而消亡
  • 引出新的通讯机制-channel

channel介绍

  • 本质是一个数据结构-队列
  • 在这里插入图片描述
  • 数据先进先出FIFO
  • 线程安全,多goroutine访问,不需要加锁
  • 多类型,一个string的channel只能存放string类型

定义/声明channel

  • var 变量名 chan 数据类型
  • var intChan chan int
  • var mapChan chan map[int]string
  • 注:channel是引用类型,必须初始化才能写入数据,即make之后才能使用
package main

import "fmt"

func main(){

	var intChan chan int
	intChan=make(chan int,3)

	fmt.Printf("intchan 的值=%v  intchan地址=%p\n",intChan,&intChan)

//	写入数据
	intChan<- 10
	num:=211
	intChan<- num
	intChan<- 50
//	不要超过容量
	fmt.Printf("channel len=%v cap=%v\n",len(intChan),cap(intChan))

//	读数据
	var num2 int
	num2=<- intChan
	fmt.Println("num2=",num2)
	fmt.Printf("channel len=%v cap=%v\n",len(intChan),cap(intChan))

//	没有使用协程的情况下,如果channel数据已经全部取出,再取就会报错
	num3:=<- intChan
	num4:=<- intChan
	num5:=<- intChan
	fmt.Println(",num3=",num3,",num4=",num4,",num5=",num5)

}

在这里插入图片描述令num5=0之后:
在这里插入图片描述

  • channel数据放满后,不能再放入
  • channel数据取出后,可以再放数据
  • 没有使用协程的情况下,channel数据取完了,再取,就会报dead lock

examples:

package main

import "fmt"

type Cat struct {
	Name string
	Age int
}

func main(){
	var mapChan chan map[string]string
	mapChan=make(chan map[string]string,10)
	m1:=make(map[string]string,20)
	m1["city1"]="北京"
	m1["city2"]="天津"

	m2:=make(map[string]string,20)
	m2["hero1"]="ldh"
	m2["hero2"]="glt"

	mapChan<- m1
	mapChan<- m2

	var catChan chan Cat
	catChan=make(chan Cat,10)
	cat1:=Cat{"tom",10}
	cat2:=Cat{"jarry",5}
	catChan<- cat1
	catChan<- cat2

	cat11:=<- catChan
	cat22:=<- catChan
	fmt.Println("cat11,cat22:",cat11,cat22)

	var catChan2 chan  *Cat
	catChan2=make(chan *Cat,10)
	catChan2<- &cat1
	catChan2<- &cat2

	cat111:=<- catChan2
	cat222:=<- catChan2
	fmt.Println("cat111,cat222:",cat111,cat222)

//	任意类型数据
	var allChan chan interface{}
	allChan=make(chan interface{},10)
	allChan<- cat1
	allChan<- cat2
	allChan<- 10
	allChan<- m1

	catt:=<- allChan
	fmt.Println("catt=",catt)
	//fmt.Println("catt.name:",catt.Name)//error: catt.Name undefined (type interface{} has no field or method Name)
	//使用类型断言
	cattt:=catt.(Cat)
	fmt.Println("cattt.name:",cattt.Name)
}

在这里插入图片描述

channel的关闭

  • 使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据,但还可以从该channel读数据
package main

import "fmt"

func main() {
	intChan :=make(chan int,10)
	intChan<- 1
	intChan<- 2
	intChan<- 3
	close(intChan)
	//intChan<- 4//报错:panic: send on closed channel
	n1:=<- intChan
	fmt.Println("n1:",n1)
	//输出:n1: 1
}

channel遍历

  • 支持for-range遍历
  • 遍历时,如果channel没有关闭,出现deadlock错误
  • 遍历时,如果channel已经关闭,会正常遍历数据
package main

import "fmt"

func main() {
	intChan :=make(chan int,20)
	for i:=0;i<20;i++{
		intChan<- i
	}
	close(intChan)
	for v:=range intChan{
		fmt.Println("v=",v)
	}
}

//输出:
//v= 0
//v= 1
//v= 2
//v= 3
//v= 4
//v= 5
//v= 6
//v= 7
//v= 8
//v= 9
//v= 10
//v= 11
//v= 12
//v= 13
//v= 14
//v= 15
//v= 16
//v= 17
//v= 18
//v= 19

案例:
在这里插入图片描述在这里插入图片描述

package main

import "fmt"

func writeData(intChan chan int){
	for i:=1;i<=50;i++{
		intChan<- i
		fmt.Println("writeData:",i)
	}
	close(intChan)
}

func readData(intChan chan int,exitChan chan bool)  {
	for{
		v,ok:=<- intChan
		if !ok{
			break
		}
		fmt.Println("readDate:",v)
	}

	exitChan<- true
	close(exitChan)
}

func main() {
	intChan :=make(chan int,50)
	exitChan:=make(chan bool,1)

	go writeData(intChan)
	go readData(intChan,exitChan)

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

Process finished with exit code 0

案例:开头的例子
统计1~8000的数字中哪些是素数

在这里插入图片描述

package main

import (
	"fmt"
	"time"
)

func putNum(intChan chan int){
	for i:=1;i<=8000;i++{
		intChan<- i
	}
	close(intChan)
}

//从intChan取出数据,并判断是否为素数,是,就放入primeChan
func primeNum(intChan chan int,primeChan chan int,exitChan chan bool){
	var flag bool
	for{
		time.Sleep(time.Millisecond*10)
		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协程因为取不到数据,退出")

	exitChan<- true
}

func main(){
	intChan:=make(chan int,1000)
	primeChan:=make(chan int,2000)
	exitChan:=make(chan bool,4)
	//开启一个协程,向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
		close(primeChan)
	}()

	//遍历primeChan,取出结果
	for{
		res,ok:=<- primeChan
		if !ok{
			break
		}
		fmt.Printf("素数=%d\n",res)
	}

	fmt.Println("main线程退出")
}

在这里插入图片描述

channel其他细节

  • channel可以声明只读或只写
  • select可解决从管道取数据阻塞的问题
    在这里插入图片描述
//默认,管道是双向
var chan1 chan int
//只写
var chan2 chan<- int
//只读
var chan1 <-chan int

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

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

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

相关文章

【数据结构】二叉树(二)

目录 一、二叉树链式结构及实现 1、二叉树的结构 2、二叉树的遍历 2.1 前序遍历 2.2 中序遍历 2.3 后序遍历 2.4 层序遍历 3、二叉树链式结构的实现 3.1 创建一个节点 3.2 二叉树节点个数 3.3 二叉树叶子节点个数 3.4 二叉树的高度 3.5 二叉树第k层节点个数 3.6 二叉树查找值…

数据库管理-第八十二期 EMCC升级教程(20230607)

数据库管理 2023-06-07 第八十二期 EMCC升级教程1 升级EMCC1.1 升级概览1.2 拷贝相关文件1.3 升级OPatch1.4 升级OMSPatcher1.5 升级WLS1.6 升级OMS 2 升级Agent2.1 升级概览2.2 拷贝相关文件2.3 安装或升级AgentPatcher2.4 升级agent 3 升级Oracle数据库ASH包总结 第八十二期 …

什么时候适合加一层?

加一层能解决问题&#xff1a; 为什么加一层能解决问题&#xff1f; 什么时候适合加一层&#xff1f; 销售说不吵的&#xff0c; 道路检测说没有超标。 业主就是睡不着。 吃瓜群众说你为啥买那边的房子。 销售说开发商骗他&#xff0c;他也是受害者。 结果没问题&#xff0…

CSS 样式语言 选择器

CSS介绍 层叠样式表&#xff0c;是一种样式表语言&#xff0c;用来描述HTML和XML文档的呈现。随着HTML的发展&#xff0c;为了满足页面设计者的要求&#xff0c;HTML添加了很多显示功能&#xff0c;但是随着这些功能的增加&#xff0c;使得HTML越来越杂乱&#xff0c;HTML 页面…

「企业安全架构」EA874:安全需求,愿景、原则和流程

安全需求愿景 在开始任何安全架构工作之前&#xff0c;定义安全需求是很重要的。这些需求应该受到业务上下文和通用需求远景文档的影响。下面是一个图表&#xff0c;它显示安全需求是企业信息安全体系结构中业务上下文的一部分。 图1 安全需求远景&#xff08;SRV&#xff09;有…

Android系统原理性问题分析 - 系统 Root 的实现原理

声明 在Android系统中经常会遇到一些系统原理性的问题&#xff0c;在此专栏中集中来讨论下。Android低版本时经常听说Root系统&#xff0c;随着Android版本的升高&#xff0c;提Root的人越来越少了。不过我在系统开发时也有客户提出为系统Root的需求&#xff0c;所以在这里分析…

【产品经理】用户增长方法论

在做用户增长为核心的产品运营推广前&#xff0c;我们应从几个方面入手——打造核心功能点、转化方式要清晰、用户反馈与转化、传播渠道要合适、建立病毒式传播规则。 2017年&#xff0c;以营销见长的可口可乐公司将设置了24年之久的首席营销官&#xff08;CMO&#xff09;撤销…

[Maven高级]->近万字文章带你深入了解Maven

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;JavaEE ⭐如果觉得文章写的不错&#xff0c;欢迎点个关注一键三连&#x1f609;有写的不好的地方也欢迎指正&…

已经安装高版本CUDA的条件下bitsandbytes发现低版本的CUDA SETUP: Detected CUDA version 100解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

Zabbix 配置钉钉报警

如有错误&#xff0c;敬请谅解&#xff01; 此文章仅为本人学习笔记&#xff0c;仅供参考&#xff0c;如有冒犯&#xff0c;请联系作者删除&#xff01;&#xff01; 1. 创建服务群【手机钉钉】|【电脑钉钉】- 右上角【】-【发起群聊】-【选人建群】/选择不同的群类型创建&…

数据库信息速递 甲骨文与微软合作,在Azure上推出数据库服务

开头还是介绍一下群&#xff0c;如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;在新加的朋友会分到2群&#xff08;共…

Linux搭建配置jdk开发环境

因为ZooKeeper、Hadoop和Spark等大数据应用的运行需要Java环境的支持&#xff0c;所以需要我们来安装配置一下jdk环境。 安装步骤如下&#xff1a; 下载JDK 访问Oracle官网下载Linux x64操作系统的JDK安装包jdk-8u161-linux-x64.tar.gz。 上传JDK安装包 通过SecureCRT远程连接…

chatgpt赋能python:Python的数据存储:理解Python的内存管理机制

Python的数据存储&#xff1a;理解Python的内存管理机制 Python是一种高级编程语言&#xff0c;广泛用于开发Web应用程序、机器学习和数据科学等。作为一门动态语言&#xff0c;Python的内存管理机制是其优点之一。这篇文章将探讨Python如何内部存储数据&#xff0c;介绍Pytho…

pytorch ddp 范例

pytorch ddp 范例&#xff1a; ################ ## main.py文件 import argparse from tqdm import tqdm import torch import torchvision import torch.nn as nn import torch.nn.functional as F # 新增&#xff1a; import torch.distributed as dist from torch.nn.paral…

从零开始手搓一个STM32与机智云的小项目——GPIO的输入输出

文章目录 前言GPIO简介GPIO的命名与数量GPIO的功能STM32F1 GPIO的寄存器 库函数开发搭建库函数的工程查看原理图WACK_UP输入按键继电器输出138控制流水灯 代码编写库函数简介GPIO输出模式控制继电器通过138控制ledGPIO实现按键输入的操作编写逻辑代码 实物效果 总结 前言 上一…

Redis学习总结(二)

AOF 为什么是在执行完命令之后记录日志&#xff1f; 关系型数据库&#xff08;如 MySQL&#xff09;通常都是执行命令之前记录日志&#xff08;方便故障恢复&#xff09;&#xff0c;而 Redis AOF 持久化机制是在执行完命令之后再记录日志。AOF 记录日志过程为什么是在执行完命…

如何让GPT不再胡说八道

相信我们大部分人在使用GPT的时候&#xff0c;会发现GPT经常在胡言乱语、回复错误的答案等情况&#xff0c;甚至有的内容牛头不对马嘴&#xff0c;直接开始编造&#xff0c;例如下面案例&#xff1a; 我&#xff1a; 周树人是谁 GPT&#xff1a;周树人 (1897年-1975年) &…

独立开发变现周刊(第90期):自学开发了一个36万美元/年的ChatGPT应用

分享独立开发、产品变现相关内容&#xff0c;每周五发布。 目录 1、ChatGPT-Midjourney: 开源 ChatGPTMidjourney 网页应用2、PLExtension: 一个图床上传浏览器扩展3、EasySpider: 一个可视化爬虫软件4、BibiGPT: 音视频 AI 一键总结 & 对话5、自学的程序员开发了一个36万美…

【i阿极送书——第四期】《ChatGPT时代:ChatGPT全能应用一本通》

系列文章目录 作者&#xff1a;i阿极 作者简介&#xff1a;数据分析领域优质创作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f44d;收藏&#x1f4c1;评论&#x1f4d2;…

MongoDB集群和安全

目录 副本集-Replica Sets简介副本集的三个角色副本集架构目标副本集的创建主节点副本节点仲裁节点初始化配置副本集和主节点查看副本集的配置内容查看副本集状态添加副本从节点添加仲裁从节点副本集的数据读写操作 主节点的选举原则完整的连接字符串 分片集群-Sharded Cluster…