Golang 学习(二)进阶使用

news2025/1/16 14:44:45

二、进阶使用

性能提升——协程
GoRoutine
go f();
  • 一个 Go 线程上,可以起多个协程(有独立的栈空间、共享程序堆空间、调度由用户控制)
  • 主线程是一个物理线程,直接作用在 cpu 上的。是重量级的,非常耗费 cpu 资源。
  • 协程从主线程开启的,是轻量级的线程,是逻辑态。对资源消耗相对小。

在这里插入图片描述

CSP并发模型

Java、C++、或者Python,他们线程间通信都是通过共享内存的方式来进行的。非常典型的方式就是,在访问共享数据(例如数组、Map、或者某个结构体或对象)的时候,通过锁来访问

Go:不要以共享内存的方式来通信,相反,要通过通信来共享内存

  • goroutine 是Go语言中并发的执行单位
  • channel是Go语言中各个并发结构体(goroutine)之前的通信机制

底层原理——MPG模型:

  • M指的是Machine,代表OS线程。它是由OS管理的执行线程,其工作方式与标准POSIX线程非常相似。在运行时代码中,它被称为M for machine。

  • P代表着处理器(processor),它的主要用途就是用来执行goroutine的,一个P代表执行一个go代码片段的基础(上下文环境),所以它也维护了一个可运行的goroutine队列,和自由的goroutine队列,里面存储了所有需要它来执行的goroutine。

  • G指的是Goroutine,代表一个goroutine。它包括堆栈,指令指针和其他对调度goroutine很重要的信息。

  • Seched代表着一个调度器,它维护有存储空闲的M队列和空闲的P队列,可运行的G队列,自由的G队列(全局runqueue)以及调度器的一些状态信息等。

操作系统会在物理处理器上调度线程来运行,而 Go 语言的运行时会在逻辑处理器上调度goroutine来运行。
p默认cpu内核数,M与P的数量没有绝对关系,一个M阻塞,P就会去创建或者切换另一个M,

img

创建一个 goroutine 并准备运行,这个 goroutine 就会被放到调度器的全局运行队列中。之后,调度器就将这些队列中的 goroutine 分配给一个逻辑处理器,并放到这个逻辑处理器对应的本地运行队列中,本地运行队列中的 goroutine 会一直等待直到自己被分配的逻辑处理器执行。

当goroutine 需要执行一个阻塞的系统调用,如打开一个文件,线程和 goroutine 会从逻辑处理器上分离,该线程会继续阻塞,等待系统调用的返回。与此同时,这个逻辑处理器就失去了用来运行的线程。所以,调度器会创建一个新线程,并将其绑定到该逻辑处理器上。之后,调度器会从本地运行队列里选择另一个 goroutine 来运行。一旦被阻塞的系统调用执行完成并返回,对应的 goroutine 会放回到本地运行队列,而之前的线程会保存好,以便之后可以继续使用。

go的协程是非抢占式的,由协程主动交出控制权,也就是说,上面在发生IO操作时,并不是调度器强制切换执行其他的协程,而是当前协程交出了控制权,调度器才去执行其他协程。我们列举一下goroutine可能切换的点:

动态获取信息——反射
  • 反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
  • 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
  • 通过反射,可以修改变量的值,可以调用关联的方法。

Type和Value:Kind是一个大的分类,比如定义了一个Person类,它的Kind就是struct 而Type的名称是Person,其中Value: 为go值提供了反射接口。

package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Name string
	Age int
}

func test(i interface{}){
	//获取指针指向的真正的数值Value
	valueOfI := reflect.ValueOf(i).Elem()
	//获取对应的Type这个是用来获取属性方法的
	typeOfI := valueOfI.Type()
	//判断是否是struct
	if typeOfI.Kind()!=reflect.Struct{
		fmt.Println("except struct")
		return
	}
	//获取属性的数量
	numField := typeOfI.NumField()
	//遍历属性,找到特定的属性进行操作
	for i:=0;i< numField;i++{
		//获得属性的StructField,次方法不同于Value中的Filed(这个返回的是Field)
		field := typeOfI.Field(i)
		//获取属性名称
		fieldName := field.Name
		fmt.Println(fieldName)
		//找到名为Name的属性进行修改值
		if fieldName=="Name"{
			//改变他的值为jack
			valueOfI.Field(i).SetString("jack")
		}
	}
}

func main() {
	stu:=Student{Name:"susan",Age:58}
	test(&stu)
	fmt.Println(stu.Name)
}
IO多路复用——select机制
select {
    case <-chan1:
        fmt.Println("chan1 ready.")
    case <-chan2:
        fmt.Println("chan2 ready.")
    default:
        fmt.Println("default")
    }

每个线程或者进程都先到注册到相应的可接受 channel,然后阻塞,当注册的线程和进程准备好数据后,channel会得到相应的数据。

  • 2)如果某个case中的channel已经ready,则执行相应的语句并退出select流程,否则:有default会走default然后退出select,没有default,select将阻塞直至channel ready;
  • 3)每个 case 语句仅能处理一个管道,要么读要么写。
  • 4)多个 case 语句的执行顺序是随机的。
  • 5)存在 default 语句,select 将不会阻塞,但是存在 default 会影响性能。
  • case后面不一定是读channel,也可以写channel,只要是对channel的操作就可以;空的select语句将被阻塞,直至panic;

使用场景:

2.1 超时控制
func (n *node) waitForConnectPkt() {
	select {
	case <-n.connected:
		log.Println("接收到连接包")
	case <-time.After(time.Second * 5):
		log.Println("接收连接包超时")
		n.conn.Close()
	}
}
2.2 无阻塞获取值
func (w *wantConn) waiting() bool {
	select {
	case <-w.ready:
		return false
	default:
		return true
	}
}
2.3 类事件驱动循环
func (n *node) heartbeatDetect() {
	for {
		select {
		case <-n.heartbeat:
			// 收到心跳信号则退出select等待下一次心跳
			break
		case <-time.After(time.Second*3):
			// 心跳超时,关闭连接
			n.conn.Close()
			return
		}
	}
}
延迟函数——defer

每个 defer 语句都对应一个_defer 实例,多个实例使用指针连接起来形成一个单连表,保存在 gotoutine 数据结构中,每次插入_defer 实例,均插入到链表的头部,函数结束再一次从头部取出,从而形成后进先出的效果。

  • 延迟函数执行按照后进先出的顺序执行,即先出现的 defer 最后执行。
  • 延迟函数可能操作主函数的返回值。
  • 申请资源后立即使用 defer 关闭资源是个好习惯。
上下文控制——Context

Go 的 Context 的数据结构包含 Deadline,Done,Err,Value,Deadline

  • Deadline 方法返回一个 time.Time,表示当前 Context 应该结束的时间
  • Done 方法当 Context 被取消或者超时时候返回的一个 close 的 channel,告诉给 context 相关的函数要停止当前工作然后返回了
  • Err 表示 context 被取消的原因
  • Value 方法表示 context 实现共享数据存储的地方,是协程安全的

应用:1:上下文控制,2:多个 goroutine 之间的数据交互等,3:超时控制:到某个时间点超时,过多久超时。

互斥锁——Mutex

1)正常模式

  • 当前的mutex只有一个goruntine来获取,那么没有竞争,直接返回。
  • 新的goruntine进来,如果当前mutex已经被获取了,则该goruntine进入一个先入先出的waiter队列,在mutex被释放后,waiter按照先进先出的方式获取锁。该goruntine会处于自旋状态(不挂起,继续占有cpu)。
  • 新的goruntine进来,mutex处于空闲状态,将参与竞争。新来的 goroutine 有先天的优势,它们正在 CPU 中运行,可能它们的数量还不少,所以,在高并发情况下,被唤醒的 waiter 可能比较悲剧地获取不到锁,这时,它会被插入到队列的前面。如果 waiter 获取不到锁的时间超过阈值 1 毫秒,那么,这个 Mutex 就进入到了饥饿模式。

2)饥饿模式

在饥饿模式下,Mutex 的拥有者将直接把锁交给队列最前面的 waiter。新来的 goroutine 不会尝试获取锁,即使看起来锁没有被持有,它也不会去抢,也不会 spin(自旋),它会乖乖地加入到等待队列的尾部。 如果拥有 Mutex 的 waiter 发现下面两种情况的其中之一,它就会把这个 Mutex 转换成正常模式:

  • 此 waiter 已经是队列中的最后一个 waiter 了,没有其它的等待锁的 goroutine 了;
  • 此 waiter 的等待时间小于 1 毫秒。
问题
  1. 是否可以对Golang中的map元素取地址?

    不可以,因为map的元素可能会因为新元素的添加或者map的扩容而被移动,所以直接获取map元素的地址可能会引用到错误的元素。

  2. Golang 调用函数传入结构体时,应该传值还是指针?

  • 结构体的大小:如果结构体非常大,使用指针传递会更有效率,因为这样只会复制指针值(一般是8字节),而不是复制整个结构体。如果结构体小,值传递和指针传递的性能差异可能可以忽略不计。
  • 是否需要修改原始结构体:如果你需要在函数中修改原始结构体,你应该使用指针传递。如果你使用值传递,函数会接收结构体的一个副本,你在函数中对结构体的修改不会影响到原始的结构体。
  1. 单引号,双引号,反引号的区别?

    单引号,表示byte类型或rune类型,对应 uint8和int32类型,默认是 rune 类型。byte用来强调数据是raw data,而不是数字;而rune用来表示Unicode的code point。双引号,才是字符串,实际上是字符数组。可以用索引号访问某字节,也可以用len()函数来获取字符串所占的字节长度。反引号,表示字符串字面量,但不支持任何转义序列。字面量 raw literal string 的意思是,你定义时写的啥样,它就啥样,你有换行,它就换行。你写转义字符,它也就展示转义字符。

  2. 怎么控制并发数量?

    有缓冲通道

    func main() {
    	count := 10 // 最大支持并发
    	sum := 100 // 任务总数
    	wg := sync.WaitGroup{} //控制主协程等待所有子协程执行完之后再退出。
    
    	c := make(chan struct{}, count) // 控制任务并发的chan
    	defer close(c)
    
    	for i:=0; i<sum;i++{
    		wg.Add(1)
    		c <- struct{}{} // 作用类似于waitgroup.Add(1)
    		go func(j int) {
    			defer wg.Done()
    			fmt.Println(j)
    			<- c // 执行完毕,释放资源
    		}(i)
    	}
    	wg.Wait()
    }
    

    第三方协程池

    import (
    	"log"
    	"time"
    
    	"github.com/Jeffail/tunny"
    )
    func main() {
    	pool := tunny.NewFunc(10, func(i interface{}) interface{} {
    		log.Println(i)
    		time.Sleep(time.Second)
    		return nil
    	})
    	defer pool.Close()
    
    	for i := 0; i < 500; i++ {
    		go pool.Process(i)
    	}
    	time.Sleep(time.Second * 4)
    }
    

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

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

相关文章

msvcp120.dll丢失如何解决/找不到msvcp120.dll的5种有效的解决方法

在计算机系统运行过程中&#xff0c;如果遇到“找不到msvcp120.dll”的提示信息&#xff0c;这代表了何种具体状况呢&#xff1f;首先&#xff0c;我们需要明确msvcp120.dll文件的重要性。msvcp120.dll是Microsoft Visual C Redistributable Package的一部分&#xff0c;这是一…

Java入门之JavaSe(韩顺平p1-p?)

学习背景&#xff1a; 本科搞过一段ACM、研究生搞了一篇B会后&#xff0c;本人在研二要学Java找工作啦~~&#xff08;宇宙尽头是Java&#xff1f;&#xff09;爪洼纯小白入门&#xff0c;C只会STL、python只会基础Pytorch、golang参与了一个Web后端项目&#xff0c;可以说项目小…

数据库学习笔记2024/2/5

2. SQL 全称 Structured Query Language&#xff0c;结构化查询语言。操作关系型数据库的编程语言&#xff0c;定义了 一套操作关系型数据库统一标准 2.1 SQL通用语法 在学习具体的SQL语句之前&#xff0c;先来了解一下SQL语言的通用语法。 1). SQL语句可以单行或多行书写&…

查大数据检测到风险等级太高是怎么回事?

随着金融风控越来越多元化&#xff0c;大数据作为新兴的技术被运用到贷前风控中去了&#xff0c;不少人也了解过自己的大数据&#xff0c;但是由于相关知识不足&#xff0c;看不懂报告&#xff0c;在常见的问题中&#xff0c;大数据检测到风险等级太高是怎么回事呢?小易大数据…

支持534种语言,开源大语言模型MaLA-500

无论是开源的LLaMA 2还是闭源的GPT系列模型&#xff0c;功能虽然很强大&#xff0c;但对语言的支持和扩展比较差&#xff0c;例如&#xff0c;二者都是以英语为主的大模型。 为了提升大模型语言的多元化&#xff0c;慕尼黑大学、赫尔辛基大学等研究人员联合开源了&#xff0c;…

第十四篇【传奇开心果系列】Python的OpenCV库技术点案例示例:图像特征提取与描述

传奇开心果短博文系列 系列短博文目录Python的OpenCV库技术点案例示例系列短博文目录前言一、OpenCV图像特征提取与描述介绍二、OpenCV图像特征提取与描述初步示例代码三、扩展思路介绍四、特征点筛选和匹配优化示例代码五、多尺度特征提取示例代码六、非局部特征描述子示例代码…

【数据结构与算法】之排序系列-20240205

这里写目录标题 一、1346. 检查整数及其两倍数是否存在二、1365. 有多少小于当前数字的数字三、1460. 通过翻转子数组使两个数组相等四、1491. 去掉最低工资和最高工资后的工资平均值五、1502. 判断能否形成等差数列 一、1346. 检查整数及其两倍数是否存在 简单 给你一个整数数…

工业笔记本丨行业三防笔记本丨亿道加固笔记本定制丨极端温度优势

工业笔记本是专为在恶劣环境条件下工作而设计的高度耐用的计算机设备。与传统消费者级笔记本电脑相比&#xff0c;工业笔记本在极端温度下展现出了许多优势。本文将探讨工业笔记本在极端温度环境中的表现&#xff0c;并介绍其优势。 耐高温性能: 工业笔记本具有更高的耐高温性…

CodeMeter强化了ETM WinCC 开放架构平台的许可与安全保护

在面对日益复杂的网络安全威胁时&#xff0c;ETM professional control采取了前瞻性的措施&#xff0c;选择了业界领先的威步CodeMeter技术&#xff0c;以保护其标志性的WinCC开放架构平台。这一选择不仅体现了ETM对安全的高度重视&#xff0c;也标志着其在保障关键基础设施运营…

见龙在田,预防性保护措施让文物传承千年

一、“见龙在田——山西出土龙文物展” “见龙在田——山西出土龙文物展”以十二生肖龙为线索&#xff0c;在山西省考古研究院&#xff08;山西考古博物馆&#xff09;揭幕&#xff0c;深入展示了中国优秀传统文化的悠久历史。展览汇聚了130多件珍贵历史文物。从新石器时代到汉…

2.0 Zookeeper 安装配置

Linux 安装 zookeeper 下载地址为: Apache ZooKeeper。 选择一稳定版本&#xff0c;本教程使用的 release 版本为3.4.14&#xff0c;下载并安装。 打开网址 https://www.apache.org/dyn/closer.lua/zookeeper/zookeeper-3.4.14/zookeeper-3.4.14.tar.gz&#xff0c;看到如下界…

Linux中有名管道和无名管道

无名管道基础 进程间通信介绍 常用通信方式 无名管道&#xff08;pipe&#xff09; 有名管道 &#xff08;fifo&#xff09; 信号&#xff08;signal&#xff09; 共享内存(mmap) 套接字&#xff08;socket&#xff09;过时的IPC通信方式 System V IPC 共享内存&#xff08;sh…

机器学习---概率图模型(隐马尔可夫模型、马尔可夫随机场、条件随机场)

1. 隐马尔可夫模型 机器学习最重要的任务是根据已观察到的证据&#xff08;例如训练样本&#xff09;对感兴趣的未知变量&#xff08;例如类别标 记&#xff09;进行估计和推测。概率模型&#xff08;probabilistic model&#xff09;提供了一种描述框架&#xff0c;将描述任…

【Qt Design】界面介绍

文章目录 前言Widget Box&#xff08;工具箱&#xff09;对象查看器Qt Design属性编译器sizePolicy内容 信号/槽编辑器资源浏览器ui文件 前言 Widget Box&#xff08;工具箱&#xff09; 提供很多控件 对象查看器 对象查看区域&#xff0c;可以查看主窗口放置对象的列表 …

HTTP相关问题

目录 1.从输入URL到页面展示到底发生了什么&#xff1f; 2.HTTP状态码有哪些&#xff1f; 2.1 2XX(成功状态码) 2.2 3XX(重定向状态码) 2.3 4XX(客户端错误状态码) 2.4 5XX(服务端错误状态码) 3.HTTP 请求头中常见的字段有哪些&#xff1f; 4.HTTP和HTTPS有什么区别&…

CSS:水平垂直居中

水平垂直居中效果如下&#xff1a; HTML: <div class"parent"><div class"child"></div> </div>公共的 CSS&#xff1a; .parent {width: 300px;height: 300px;background-color:#d0e4fe; }.child {width: 100px;height: 100px…

第二篇:MySQL安装与配置(基于小皮面板(phpstudy))

在第一篇中介绍了数据库的相关概念&#xff0c;了解到SQL是用来操作数据库管理系统的语言&#xff0c;因此要学习数据库技术&#xff0c;数据库管理系统的配备是必不可少的&#xff01; 并且出于流行性与实惠性的双考量而选择MySQL这款数据库管理系统软件 一&#xff0c;工具推…

centos安装inpanel

前置条件 安装python yum -y install python 安装 cd /usr/local git clone https://gitee.com/WangZhe168_admin/inpanel.git cd inpanel python install.py 安装过程需要设置账户 密码 端口号 我设置的是admin:admin 10050 使用 打开浏览器,输入 http://192.168.168.…

mysql 多数据源

依赖 <dependencies><!--mysql连接--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--多数据源--><dependency><g…

Node.js+Express+Mysql服务添加环境变量

1、使用dotenv插件 1&#xff09;安装插件&#xff1a;npm install dotenv-cli --save-dev 2&#xff09;在项目根目录下添加对应的 .env 配置文件&#xff1b; // .env配置文件内容 MODEdevelopment, BASE_URLhttp://127.0.0.1:80813) 在启动命令中设置对应的加载文件&#…