深入解析 Go 语言 GMP 模型:并发编程的核心机制
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:点击跳转到网站,对人工智能感兴趣的小伙伴可以点进去看看。
前言
本章是Go并发编程的起始篇章,在未来几篇文章中我们会围绕Go并发编程进行理论和实战的学习,欢迎关注我哦!
本章主要以介绍GMP模型为主,偏向于面试和八股,目的是让小伙伴们注重于知识本身,面向面试,面向八股,面向加薪。
Go语言自诞生以来,就以其简洁、高效的并发模型著称。而这其中的核心正是GMP模型。理解GMP模型的演进历程,能帮助我们更好地掌握Go的并发编程。而Goroutine作为Go中的核心概念,极大地简化了并发编程的复杂度。本文将详细阐述Go语言GMP模型的演变过程,并深入解析其设计理念和优点,并详细介绍Goroutine的基本概念、优势及其使用方法,并结合具体代码示例进行说明。
面试题目
在阅读本文前,先带着以下几个关于GMP模型的面试题目进行思考,以加深理解和掌握:
- 什么是GMP模型?请解释其基本概念。
- 回答要点:解释G、M、P的概念及其在调度模型中的角色。
- 如何理解GMP模型中线程的内核态和用户态?
- 回答要点:区分内核态线程和用户态线程,并说明它们在GMP模型中的作用。
- Go语言中的Goroutine与线程的映射关系是怎样的?为什么选择这种映射方式?
- 回答要点:解释Goroutine与线程的多对多映射关系及其优点。
- GMP模型如何解决线程调度中的锁竞争问题?
- 回答要点:介绍全局队列和本地队列的使用,以及G的分配机制。
- GMP模型中的Stealing机制是什么?它如何工作?
- 回答要点:描述Stealing机制的原理及其在Goroutine调度中的应用。
- 什么是Hand off机制?在什么情况下会使用该机制?
- 回答要点:解释Hand off机制及其在阻塞和系统调用中的应用。
- 如何理解GMP模型中的抢占式调度?它解决了哪些问题?
- 回答要点:说明抢占式调度的原理及其在防止协程饿死中的作用。
- 什么是G0和M0?它们在GMP模型中扮演什么角色?
- 回答要点:描述G0和M0的定义及其在Goroutine调度中的功能。
- 请详细说明GMP模型中的调度策略。
- 回答要点:逐步解释Goroutine的创建、唤醒、偷取、切换、自旋、系统调用和阻塞处理策略。
- 如何在实际项目中调优GMP调度模型?
- 回答要点:讨论如何通过调整GOMAXPROCS等参数来优化调度性能。
带着这些问题阅读本文,可以帮助你更系统地掌握GMP模型的核心概念和调度机制,提高面试中的应答能力。
单进程时代
基本概念
在单进程时代,一个进程就是一个运行中的程序。计算机系统在执行程序时,会从头到尾依次执行完一个程序,然后再执行下一个程序。在这种模型中,不需要复杂的调度机制,因为只有一个执行流程。
面临的两个问题
- 单一执行流程:由于只能一个个执行程序,无法同时处理多个任务,这大大限制了CPU的利用率。
- 进程阻塞:当一个进程遇到I/O操作等阻塞情况时,CPU资源会被浪费,等待进程完成阻塞操作后再继续执行,导致效率低下。
多进程/线程并发时代
基本概念
为了解决单进程时代的效率问题,引入了多进程和多线程并发模型。在这种模型中,当一个进程阻塞时,CPU可以切换到另一个准备好的进程继续执行。这样可以充分利用CPU资源,提高系统的并发处理能力。
两个问题
- 高开销:进程拥有大量资源,进程的创建、切换和销毁都需要消耗大量的时间和资源。这导致CPU很大一部分时间都在处理进程调度,而不是实际的任务执行。
- 高内存占用:在32位机器下,进程的虚拟内存占用为4GB,线程占用为4MB。大量的线程和进程会导致高内存消耗,限制了系统的扩展性。
协程的引入
为了解决多进程和多线程带来的高开销和高内存占用问题,引入了协程(Coroutine)。协程是一种比线程更轻量级的执行单元。协程在用户态进行调度,避免了频繁的上下文切换带来的开销。Go语言的GMP模型正是基于协程的设计。
协程的基本概念
在深入了解Goroutine之前,先来了解一下协程(Coroutine)的基本概念。
内核态和用户态
- 内核态线程:由操作系统管理和调度,CPU只负责处理内核态线程。
- 用户态线程:由用户程序管理,需绑定到内核态线程上执行,协程即为用户态线程的一种。
内核态和用户态线程关系图
- Kernel Space(内核空间):上半部分的灰色区域,表示操作系统管理的内核空间。
- User Space(用户空间):下半部分的白色区域,表示用户程序运行的空间。
- Kernel Thread 1 和 Kernel Thread 2(内核线程):由操作系统管理的内核线程,CPU直接处理这些线程。
- User Thread 1、User Thread 2 和 User Thread 3(用户线程):由用户程序管理的用户线程(协程),需绑定到内核线程上执行。
执行流程
-
用户态线程:
- 用户程序创建多个用户线程(如协程),如图中的“User Thread 1”、“User Thread 2”和“User Thread 3”。
-
内核态线程:
- 用户线程需绑定到内核态线程上执行,如图中的“Kernel Thread 1”和“Kernel Thread 2”。
-
CPU处理:
- CPU只处理内核态线程,通过绑定关系,用户态线程的执行也依赖于内核态线程的调度。
- 图中的红色箭头表示CPU正在处理内核线程,从而间接处理绑定的用户线程。
线程和协程的映射关系
- 单线程绑定所有协程:
- 问题1:无法利用多核CPU的能力。
- 问题2:如果某个协程阻塞,整个线程和进程都将阻塞,导致其他协程无法执行,丧失并发能力。
- 一对一映射:
- 将每个协程绑定到一个线程上,退回到多进程/线程的模式,协程的创建、切换、销毁均需CPU完成,效率低下。
- 多对多映射:
- 允许多个协程绑定到多个线程上,形成M:N的关系。这样可以充分利用多核CPU,并通过协程调度器高效管理协程的执行。
Goroutine
Goroutine是Go语言中的协程,实现了轻量级并发。与传统的线程相比,Goroutine具有以下显著特点:
轻量级
Goroutine非常轻量,初始化时仅占用几KB的栈内存,并且栈内存可以根据需要动态伸缩。这使得我们可以在Go程序中创建成千上万个Goroutine,而不会消耗过多的系统资源。
高效调度
Goroutine的调度由Go语言的运行时(runtime)负责,而不是操作系统。Go运行时在用户态进行调度,避免了频繁的上下文切换带来的开销,使得调度更加高效。
Goroutine的使用示例
下面是一个简单的示例,展示了如何在Go语言中使用Goroutine进行并发编程。
package mainimport ("fmt""time"
)func say(s string) {for i := 0; i < 5; i++ {time.Sleep(100 * time.Millisecond)fmt.Println(s)}
}func main() {go say("Hello")go say("World")time.Sleep(1 * time.Second)fmt.Println("Done")
}
在这个示例中,两个Goroutine同时执行,分别打印"Hello"和"World"。通过使用go
关键字,我们可以轻松地启动一个新的Goroutine。
需要注意的事项
- 主Goroutine的结束:在Go程序中,main函数本身也是一个Goroutine,称为主Goroutine。当主Goroutine结束时,所有其他Goroutine也会随之终止。因此,需要确保主Goroutine等待所有子Goroutine执行完毕。
- 同步和共享数据:虽然Goroutine之间共享内存空间,但需要通过同步机制(如通道和锁)来避免竞争条件。Go语言推荐使用通道(channel)进行Goroutine之间的通信,以保证数据的安全性和同步性。
示例:使用通道进行同步
下面的示例展示了如何使用通道来同步多个Goroutine的执行。
package mainimport ("fmt""sync"
)func worker(id int, wg *sync.WaitGroup) {defer wg.Done()fmt.Printf("Worker %d starting\n", id)// 模拟工作fmt.Printf("Worker %d done\n", id)
}func main() {var wg sync.WaitGroupfor i := 1; i <= 5; i++ {wg.Add(1)go worker(i, &wg)}wg.Wait()fmt.Println("All workers done")
}
在这段代码中,使用sync.WaitGroup
来同步多个Goroutine。主Goroutine启动多个子Goroutine并等待它们完成,每个子Goroutine在完成任务后调用wg.Done()
减少计数,主Goroutine调用wg.Wait()
阻塞等待所有子Goroutine完成。
执行流程
- 主Goroutine启动多个子Goroutine(Goroutine 1、2、3)。
- 各个Goroutine并发执行它们的任务。
- 每个Goroutine在完成任务后,向通道发送信号表示已完成。
- 主Goroutine通过通道接收所有子Goroutine的完成信号,然后继续执行。
Goroutine执行与同步流程图
这张图展示了多个Goroutine同时执行的流程以及如何通过通道(Channel)进行同步。
- Goroutine 1、2、3:代表多个并发执行的Goroutine,分别标记为“Goroutine 1”、“Goroutine 2”和“Goroutine 3”。
- Main Goroutine:主Goroutine,它负责启动其他Goroutine并等待它们完成。
- Channel:用于同步Goroutine的通道。
关于waitgroup我会在下一章节中进行详细讲解,欢迎订阅我的频道!在本实例代码中大家了解使用即可。
Goroutine调度器
基本概念
在Go中,线程是运行Goroutine的实体,而调度器的功能是将可运行的Goroutine分配到工作线程上。Go语言采用了一种高效的Goroutine调度机制,使得程序能够在多核处理器上高效运行。
被废弃的调度器
早期的调度器采用了简单的设计,存在多个缺陷:
-
概念:用大写的G表示协程,用大写的M表示线程。
-
问题:
- 锁竞争:每个M(线程)想要执行、放回G(协程)都必须访问一个全局G队列,因此对G的访问需要加锁以保证并发安全。当有很多线程时,锁竞争激烈,影响系统性能。
- 局部性破坏:M转移G会造成延迟和额外的系统负载。例如,当一个G内创建另一个G’时,为了继续执行G,需要将G’交给另一个M’执行,这会破坏程序的局部性。
- 系统开销:CPU在线程之间频繁切换导致频繁的系统调用,增加了系统开销。
GMP模型的设计思想
为了克服上述问题,Go引入了GMP模型:
-
基本概念:
-
Go语言使用GMP模型来管理并发执行。GMP模型由三个核心组件组成:G(Goroutine)、M(Machine)、P(Processor)。
G(Goroutine)
Goroutine是Go语言中的协程,代表一个独立的执行单元。Goroutine比线程更加轻量级,启动一个Goroutine的开销非常小。Goroutine的调度由Go运行时在用户态进行。
M(Machine)
M代表操作系统的线程。M负责实际执行Go代码。一个M可以执行多个Goroutine,但同一时间只能执行一个Goroutine。M与操作系统的线程直接对应,Go运行时通过M来利用多核CPU的并行计算能力。
P(Processor)
P代表执行上下文(Processor)。P管理着可运行的Goroutine队列,并负责与M进行绑定。P的数量决定了可以并行执行的Goroutine的数量。Go运行时会根据系统的CPU核数设置P的数量。
-
-
GMP模型的组成:
- 全局G队列:存放等待运行的G。
- P的本地G队列:存放不超过256个G。当新建协程时优先将G存放到本地队列,本地队列满了后将一半的G移动到全局队列。
- M:内核态线程,线程想要运行协程需要先获取一个P,从P的本地G队列中获取G。当本地队列为空时,会尝试从全局队列或其他P的本地G列表中偷取G。
- P列表:程序启动时创建GOMAXPROCS个P,并保存在数组中。
-
调度器与OS调度器结合:Go的Goroutine调度器与操作系统调度器结合,OS调度器负责将线程分配给CPU执行。
设计策略
- 复用线程的两个策略:
- Work Stealing机制:当本线程没有可执行的G时,优先从全局G队列中获取一批G。如果全局队列中没有,则尝试从其他P的G队列中偷取G。
- Hand Off机制:当本线程因G进行系统调用等阻塞时,线程会释放绑定的P,把P转移给其他空闲的M执行。
- 利用并行:有GOMAXPROCS个P,则可以有同样数量的线程并行执行。
- 抢占式调度:Goroutine是协作式的,一个协程只有让出CPU才能让下一个协程执行,而Goroutine执行超过10ms就会强制让出CPU,防止其他协程饿死。
- 特殊的G0和M0:
- G0:每次启动一个M都会创建的第一个Goroutine,仅用于调度,不指向任何可执行的函数。每个M都有一个自己的G0,在调度或系统调用时使用G0的栈空间。
- M0:启动程序后的第一个主线程,负责执行初始化操作和启动第一个Goroutine,此后与其他M一样。
调度策略
-
创建两步:
- 通过
go func()
创建一个协程。 - 新创建的协程优先保存在P的本地G队列,如果本地队列满了,会将P本地队列中的一半G打乱顺序移入全局队列。
- 通过
- 唤醒获取:
- 创建G时运行的G会尝试唤醒其他的PM组合去执行。假设G2唤醒了M2,M2绑定了P2,但P2本地队列没有G,此时M2为自旋线程。M2便会尝试从全局队列中获取G。
- 偷取:
- 假设P的本地队列和全局队列都空了,会从其他P偷取一半G到自己的本地队列执行。
- 切换逻辑:
- G1运行完后,M上运行的协程切换回G0,G0负责调度时协程的切换。先从P的本地队列获取G2,从G0切换到G2,从而实现M的复用。
- 自旋:
- 自旋线程会占用CPU时间,但创建销毁线程也会消耗CPU时间,系统最多有GOMAXPROCS个自旋线程,其余的线程会在休眠M队列里。
- 系统调用:
- 当G进行系统调用时会进入内核态被阻塞,GM会绑定在一起进行系统调用。M会释放绑定的P,把P转移给其他空闲的M执行。当系统调用结束时,GM会尝试获取一个空闲的P。
- 阻塞处理:
- 当G因channel或network I/O阻塞时,不会阻塞M,当超过10ms时M会寻找其他可运行的G。
- 公平性:
- 调度器每调度61次时,会尝试从全局队列里取出待运行的Goroutine来运行,如果没有找到,就去其他P偷一些Goroutine来执行。
GMP模型的优势
- 高效的资源利用:通过在用户态进行调度,避免了频繁的上下文切换带来的开销,充分利用CPU资源。
- 轻量级并发:Goroutine比线程更加轻量级,可以启动大量的Goroutine而不会消耗大量内存。
- 自动调度:Go运行时自动管理Goroutine的调度,无需程序员手动干预,简化了并发编程的复杂度。
面试题
如果问到了说一说GMP调度模型,建议需要说的内容
在面试中,如果被问到GMP调度模型,建议全面地回答以下内容。如果能完整且详细地讲述这些内容,将会展示你对GMP调度模型的深刻理解和熟练掌握,这将是面试中的亮点。
基本概念
- 线程的内核态和用户态:
- 线程分为“内核态”和“用户态”。用户态线程即协程,必须绑定一个内核态线程。CPU只负责处理内核态线程。
- 调度器:
- 在Go中,线程是运行Goroutine的实体。调度器的功能是将可运行的Goroutine分配到工作线程上。
- 映射关系:
- 在Go语言中,线程与协程的映射关系是多对多的。这样避免了多个协程对应一个线程时出现的无法使用多核和并发的问题。Go的协程是协作式的,只有让出CPU资源才能调度。如果一个协程阻塞,只有一个线程在运行,其他协程也会被阻塞。
四个概念
- 全局队列:
- 存放等待运行的Goroutine。
- 本地队列:
- 每个P(处理器)都有一个本地队列,存放不超过256个Goroutine。新建协程时优先放入本地队列,本地队列满了则将一半的G移入全局队列。
- GMP:
- G:Goroutine,Go语言中的协程。
- M:Machine,内核态线程,运行Goroutine的实体。
- P:Processor,处理器,包含运行Goroutine的资源和本地队列。
设计策略
- 复用线程:
- Stealing机制:当一个线程没有可执行的G时,会从全局队列或其他P的本地队列中偷取G来执行。
- Hand off机制:当一个线程因G进行系统调用等阻塞时,线程会释放绑定的P,把P转移给其他空闲的M执行。
- P并行:
- 有GOMAXPROCS个P,代表最多有这么多个线程并行执行。
- 抢占式调度:
- Goroutine执行超过10ms就会强制让出CPU,防止其他协程饿死。
- 特殊的G0和M0:
- G0:每个M启动时创建的第一个Goroutine,仅用于调度,不执行用户代码。每个M都有一个G0。
- M0:程序启动后的第一个主线程,负责初始化操作和启动第一个Goroutine。
调度策略
- 创建:
- 通过
go func()
创建一个协程。新创建的协程优先保存在P的本地G队列,如果本地队列满了,会将P本地队列中的一半G移入全局队列。
- 唤醒:
- 创建G时,当前运行的G会尝试唤醒其他PM组合执行。若唤醒的M绑定的P本地队列为空,M会尝试从全局队列获取G。
- 偷取:
- 如果P的本地队列和全局队列都为空,会从其他P偷取一半G到自己的本地队列执行。
- 切换:
- G1运行完后,M上运行的Goroutine切换回G0,G0负责调度协程的切换。G0从P的本地队列获取G2,实现M的复用。
- 自旋:
- 自旋线程会占用CPU时间,但创建销毁线程也消耗CPU时间。系统最多有GOMAXPROCS个自旋线程,其他线程在休眠M队列里。
- 系统调用:
- 当G进行系统调用时进入内核态被阻塞,M会释放绑定的P,把P转移给其他空闲的M执行。当系统调用结束,GM会尝试获取一个空闲的P。
- 阻塞处理:
- 当G因channel或network I/O阻塞时,不会阻塞M。超过10ms时,M会寻找其他可运行的G。
- 公平性:
- 调度器每调度61次时,会尝试从全局队列中取出待运行的Goroutine来运行。如果没有找到,就去其他P偷一些Goroutine来执行。
欢迎关注 ❤
我们搞了一个免费的面试真题共享群,互通有无,一起刷题进步。
没准能让你能刷到自己意向公司的最新面试题呢。
感兴趣的朋友们可以加我微信:wangzhongyang1993,备注:csdn面试群。
相关文章:
深入解析 Go 语言 GMP 模型:并发编程的核心机制
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家:点击跳转到网站,对人工智能感兴趣的小伙伴可以点进去看看。 前言 本章是Go并发编程的起始篇章,在未来几篇文章中我们会…...
PHP中如何处理字符串
在PHP中,处理字符串是一项非常常见的任务,PHP提供了大量的内置函数来方便地处理字符串。以下是一些常用的字符串处理函数: strlen() - 返回字符串的长度。 php复制代码 $text "Hello, World!"; echo strlen($text); // 输出&…...
windows内存泄漏检查汇总
VLD(Visual Leak Detector) 下载 官方下载地址2.5 另一分支2.7 安装 点击运行安装...
yolo格式数据集之空中及地面拍摄道路病害检测7种数据集已划分好|可以直接使用|yolov5|v6|v7|v8|v9|v10通用
yolo格式数据集之空中及地面拍摄道路病害检测7种数据集已划分好|可以直接使用|yolov5|v6|v7|v8|v9|v10通用 本数据为空中及地面拍摄道路病害检测检测数据集,数据集数量如下: 总共有:33585张 训练集:6798张 验证集:3284张 测试集&a…...
[Meachines] [Easy] Mirai Raspberry树莓派默认用户登录+USB挂载文件读取
信息收集 IP AddressOpening Ports10.10.10.48TCP:22,53,80,1276,32400,32469 $ nmap -p- 10.10.10.48 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 6.7p1 Debian 5deb8u3 (protocol 2.0) | ssh-hostkey: | 1024 aa:ef:5c:…...
从零开始安装Jupyter Notebook和Jupyter Lab图文教程
前言 随着人工智能热浪(机器学习、深度学习、卷积神经网络、强化学习、AGC以及大语言模型LLM, 真的是一浪又一浪)的兴起,小伙伴们Python学习的热情达到了空前的高度。当我20年前接触Python的时候,做梦也没有想到Python会发展得怎么…...
数据库魔法:SQL Server中自定义分区函数的奥秘
数据库魔法:SQL Server中自定义分区函数的奥秘 在SQL Server中,分区表是管理大型表和提高查询性能的强大工具。分区函数和分区方案允许你根据特定的规则将数据分散到不同的文件组中。本文将深入探讨如何在SQL Server中实现数据库的自定义分区函数&#…...
网页禁止移除水印
一般的话水印分为明水印和暗水印两种 明水印的话就是在视频canvas上面蒙上一个div(如我上篇文章) ,暗水印的话就是把文字通过技术嵌入到图像里。 具体实现的话可以使用MutationObserver API 来监视 DOM 的变化,特别是针对目标节…...
Node Red 与axios简易测试环境的搭建
为了学习在vue3中如何使用axios,我借Sider Fusion的帮助搭建了基于node的简易测试环境。 Axios 是一个基于 Promise 的 HTTP 客户端,通常用于浏览器环境,但它也可以在 Node.js 环境中使用。因此,可以在 Ubuntu 的 Bash 环境下通过…...
测试面试宝典(四十三)—— 接口测试流程
回答一: 接口测试一般遵循以下流程: 需求分析 仔细研究接口的需求文档,包括接口的功能、输入输出参数、业务逻辑、性能要求等。与开发人员、产品经理等沟通,确保对需求的理解准确无误。 测试计划制定 确定测试的目标、范围和策略。…...
arkhamintelligence 请求头加密 X-Payload 完整逆向分析+自动化解决方案
大家好!我是爱摸鱼的小鸿,关注我,收看每期的编程干货。 逆向是爬虫工程师进阶必备技能,当我们遇到一个问题时可能会有多种解决途径,而如何做出最高效的抉择又需要经验的积累。本期文章将以实战的方式,带你详细地逆向分析 arkhamintelligence 请求头加密字段 X-Payload 的…...
Vue Router哈希模式和历史模式
Vue官方文档 哈希模式(hash mode) 特点 URL 格式:使用 # 符号分隔路径,哈希值之后的部分由客户端解析。 https://example.com/#/about无需服务器配置:哈希值部分不会被发送到服务器,因此不需要额外的服…...
Springboot实战:AI大模型+亮数据代理助力短视频时代
目录 前言1.如何入门亮数据1.1、注册登录1.2、注册账号1.3、登录1.4、购买静态住宅代理1.5、展示购买的代理 2. 使用Springboot、AI大模型构建系统2.1 使用Springboot、AI大模型构建爬虫2.2、在Springboot项目添加工具 3、编写代码,爬取视频素材3.1、代码里使用代理…...
Postman请求问题 connect ECONNREFUSED 127.0.0.1:80解决方法
问题描述: 解决方法: (1)点击file-settings (2)点击Proxy,并将右边的Use the system proxy 取消选中 (3)勾选use custom proxy configuration 这个8080是默认的…...
维护SQL Server数据库索引:保持性能的黄金法则
维护SQL Server数据库索引:保持性能的黄金法则 在SQL Server中,数据库索引是优化查询性能的关键工具。然而,随着数据的不断变化,索引可能会变得碎片化或过时,从而降低数据库性能。因此,定期维护索引是确保…...
nvm管理node版本问题处理集合
windows上通过nvm管理node版本,通过nvm安装node,报错了,信息: > Could not retrieve https://nodejs.org/dist/latest/SHASUMS256.txt. Get > https://nodejs.org/dist/latest/SHASUMS256.txt: dial tcp 104.20.23.46:443: …...
word打印---doc转html后进行打印,window.print、print-js、vue-print-nb
提示:word预览方式—插件 文章目录 [TOC](文章目录) 前言一、vue-office-docx把docx转换html二、调取window.print三、print-js四、vue-print-nb总结 前言 word预览 一、vue-office-docx把docx转换html npm install vue-office-docx -S-DofficeDocx.vue <templ…...
CTF学习笔记汇总(非常详细)零基础入门到精通,收藏这一篇就够了
CTF学习笔记汇总 Part.01 Web 01 SSRF 主要攻击方式如下: 01 对外网、服务器所在内网、本地进行端口扫描,获取一些服务的banner信息。 02 攻击运行在内网或本地的应用程序。 03 对内网Web应用进行指纹识别,识别企业内部的资产信息。 …...
如果想不明白,那就读书吧
人生起伏是常态,平平淡淡的日子亦是常态,但是在常态中的普通人往往面对着各种各样的风险和挑战,稍有不慎,生活天翻地覆。 回到现在这家公司是一次吃回头草的过程,其中亦是一次生活的坎坷,祸福相伴。来公司…...
Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签
本教程主要实现【Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签】。 本文源码:https://gitee.com/songfayuan/go-zero-demo 教程源码分支:master 分支(_examples/word-template/fill-word-template.go&…...
PHP学习:PHP基础
以.php作为后缀结尾的文件,由服务器解析和运行的语言。 一、语法 PHP 脚本可以放在文档中的任何位置。 PHP 脚本以 <?php 开始,以 ?> 结束。 <!DOCTYPE html> <html> <body><h1>My first PHP page</h1><?php …...
Xinstall新玩法:Web直接打开App,用户体验再升级!
在移动互联网时代,App已成为我们日常生活中不可或缺的一部分。然而,在App推广和运营过程中,许多开发者面临着从Web端引流到App的难题。这时,Xinstall作为国内专业的App全渠道统计服务商,提供了一种创新的解决方案——通…...
perf 排查高延迟
高延迟的 2 个场景,触发 perf 录包思路 当前 perf 没有常驻内存,后续提供 perf 常驻内存功能。且 perf 启动需要 0.5~1s,所以,存在 2 个场景 1.频繁连续高延迟(复现后的几秒内,继续频繁复现)&a…...
配置8188eu无线网卡的热点模式
下载驱动 github:8188eu的最新驱动,注意下载5.2.2.4分支 关于这一驱动,不要下载master分支,master分支代码较早,会导致以下两点问题: 1.STA模式下连接wifi信号较差时会卡死 2.无法启动AP模式 所以请务必下载5.2.2.4分…...
为什么 DDoS 攻击偏爱使用 TCP 和 UDP 包?
Distributed Denial of Service (DDoS) 攻击是指攻击者利用多个计算机系统或网络设备(通常是被恶意软件感染的计算机,被称为“僵尸网络”)来淹没目标服务器的资源,导致合法用户无法访问服务。TCP 和 UDP 是两种最常见的用于 DDoS …...
多址技术(FDMA,TDMA,CDMA,帧,时隙)(通俗易懂)
多址技术是一种区分用户的技术。 举个例子,一个基站发出信息,如何确定是发给谁的? 这个技术就是解决这个问题的。 多址技术常见的有三种: 频分多址(FDMA)、时分多址(TDMA)、码分…...
基于 KubeSphere 的 Kubernetes 生产环境部署架构设计及成本分析
转载:基于 KubeSphere 的 Kubernetes 生产环境部署架构设计及成本分析 前言 导图 1. 简介 1.1 架构概要说明 今天分享一个实际小规模生产环境部署架构设计的案例,该架构设计概要说明如下: 本架构设计适用于中小规模(<50)的 Kubernetes …...
RabbitMQ 入门篇
接上一篇《RabbitMQ-安装篇(阿里云主机)-CSDN博客》 安装好RabbitMQ后,我们将开始RabbitMQ的使用,根据官网文档RabbitMQ Tutorials | RabbitMQ,我们一步一步的学习。 1. "Hello World!" 这里先说明几个概…...
【赛事】2024第五届“华数杯”全国大学生数学建模竞赛
为了培养学生的创新意识及运用数学方法和计算机技术解决实际问题的能力,天津市未来与预测科学研究会、中国未来研究会大数据与数学模型专业委员会(协办)决定举办华数杯全国大学生数学建模竞赛。竞赛的目标是为培养大学生的科学精神及运用数学…...
DB管理客户端navicat、DBever、DbVisualizer数据库连接信息迁移
DB管理客户端Navicat、DBever、DbVisualizer数据库连接信息迁移 第三方数据库连接工具为了确保数据库信息安全通常对保存的数据库连接密码进行加密,填入后想再拿到原文就不可能了,有时交接给别人或者换电脑时可以通过连接数据导出的方式来解决。 Navic…...
网站建设以及运营方面/乐事薯片软文推广
是不是觉得target有点眼熟?! 今天要讲的不是HTML的<a>标签里面有个target属性。 target伪类是css3的新属性。 说到伪类,对css属性的人肯定都知道:hover、:link、:visited、:focus等等,target用法跟他们是同出一辙的。 官方定义是: URL…...
做app和做网站/广州网络推广平台
公元二零一二年,此猴子在此国度出世。掌握内功心法为传说中的特级心法《道难特》另外了解过另一特级心法为《甲骨文之佳万》。为此在纠结以后的拜师之路,请各位大侠帮助,谢谢。转载于:https://www.cnblogs.com/ja-net/archive/2012/05/10/249…...
昆明网站建设-中国互联/百度助手应用商店下载安装
这篇文章主要介绍了PhpstormXdebug断点调试PHP的方法,本教程将通过配置Xdebug扩展进行断点调试,目的在于提高大家的开发效率,感兴趣的小伙伴们可以参考一下为什么使用断点调试大家可能会觉得使用var_dump和echo也能调试啊,为什么还…...
wordpress网站自动伪原创/品牌推广方式有哪些
KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt同时发现,因此人们称它为克努特——莫里斯——普拉特操作(简称KMP算法)。KMP算法的关键是利用匹配失败后的信息,尽量减少模式串与主串…...
wordpress插件的用法/百度发视频步骤
父组件向子组件传值:父组件通过属性向下传值的方式和子组件通信;使用步骤:定义组件:现有自定义组件com-a、com-b,com-a是com-b的父组件;准备获取数据:com-b要获取父组件data中的name属性&#x…...
数据库网站建设公司/seo经理招聘
要实现如题的效果,可以利用表格来对图片进行排版,方法分为九步,具体如下:第一步:新建或打开Word文档,插入一个两行多列的表格(表格列数取决于图片的数量),如图。第二步:全选表格-右键…...