Golang--协程和管道

news2024/11/27 4:00:11

1、概念

程序:
是为完成特定任务、用某种语言编写的一组指令的集合,是一段静态的代码。(程序是静态)

进程:
程序的一次执行过程。正在运行的一个程序,进程作为资源分配的单位,在内存中会为每个进程分配不同的内存区域,是一个动的过程 ,进程有它自身的产生、存在和消亡的过程。(进程是动态的)

线程:
进程可进一步细化为线程,是一个程序内部的一个执行路径。若一个进程同一时间并执行多个线程,就是支持多线程的。 
单核:并发执行
多核:并发执行和并行执行

协程:

又称为微线程,纤程,协程是一种用户态的轻量级线程

作用:在执行A函数的时候,可以随时中断,去执行B函数,然后中断继续执行A函数(可以自动切换),注意这一切换过程并不是函数调用(没有调用语句),过程很像多线程,然而协程中只有一个线程在执行(协程的本质是个单线程)

对于单线程下,我们不可避免程序中出现io操作,但如果我们能在自己的程序中(即用户程序级别,而非操作系统级别)控制单线程下的多个任务能在一个任务遇到io阻塞时就将寄存器上下文和栈保存到某个其他地方,然后切换到另外一个任务去计算。在任务切回来的时候,恢复先前保存的寄存器上下文和栈,这样就保证了该线程能够最大限度地处于就绪态,即随时都可以被cpu执行的状态,相当于我们在用户程序级别将自己的io操作最大限度地隐藏起来,从而可以迷惑操作系统,让其看到:该线程好像是一直在计算,io比较少,从而会更多得将cpu执行权限分配给我们的线程(线程是cpu控制的,而协程是程序自身控制的,属于程序级别的切换,操作系统完全感知不到,因而更加轻量级

2、启动一个协程 

案例:
编写一个程序,完成如下功能:

  1. 在主线程中,开启一个goroutine(协程),该goroutine每隔1秒输出"hello golang"
  2. 在主线程中也每隔一秒输出"hello world",输出10次后退出程序
  3. 要求主线程和goroutine同时执行
package main
import (
	"fmt"
	"strconv"
	"time"
)

func test(){
	for i := 1; i <= 10; i++{
		fmt.Println("hello golang" + strconv.Itoa(i))
		//阻塞一秒
		time.Sleep(time.Second * 1)// 1s
	}
}

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

	for i := 1; i <= 10; i++{
		fmt.Println("hello world" + strconv.Itoa(i))
		//阻塞一秒
		time.Sleep(time.Second * 1)// 1s
	}
}

开启协程:使用go关键字,例如:go test()

执行流程:
注意:如果主线程退出,协程还没执行结束,协程也会提前结束(主死从随)

3、启动多个协程

package main
import (
	"fmt"
	"strconv"
	"time"
)

func test1(){
	for i := 1; i <= 10; i++{
		fmt.Println("hello golang" + strconv.Itoa(i))
		//阻塞一秒
		time.Sleep(time.Second * 1)// 1s
	}
}

func main(){   //主线程

	//开启一个协程--使用匿名函数
	//匿名函数 + 外部变量 = 闭包
	for i := 1; i <= 5; i++{
		go func(n int){
			fmt.Println(n)
		}(i)
	}

	//开启一个协程--使用普通函数
	go test1()

	for i := 1; i <= 10; i++{
		fmt.Println("hello world" + strconv.Itoa(i))
		//阻塞一秒
		time.Sleep(time.Second * 1)// 1s
	}
}

4、使用WaitGroup控制协程退出

WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量,每个被等待的线程在结束时应调用Done方法。同时主线程里可以调用Wait方法阻塞至所有线程结束。用于解决主线程在子协程结束后自动结束,防止主线程退出导致协程被迫退出,


package main
import (
	"fmt"
	"sync" // 并发包
	"time"
	"strconv"
)

var wg sync.WaitGroup // 并发包的变量, 只定义无需初始化--类似计数器

func test1(){
	//协程执行完毕,协程数量-1
	defer wg.Done()
	for i := 1; i <= 10; i++{
		fmt.Println("hello golang" + strconv.Itoa(i))
		//阻塞一秒
		time.Sleep(time.Second * 2)// 2s
	}
	// //协程执行完毕,协程数量-1
	// wg.Done()
}

func main(){   //主线程
	//开启一个协程--使用普通函数
	wg.Add(1)  // 协程数量+1
	go test1() // 开启协程
	//主线程一直阻塞,等待协程执行完毕--直到协程执行完毕才会继续执行主线程
	wg.Wait()
	
	for i := 1; i <= 10; i++{
		fmt.Println("hello world" + strconv.Itoa(i))
		//阻塞一秒
		time.Sleep(time.Second * 1)// 1s
	}
}

5、多个协程操纵同一数据案例(使用互斥锁同步协程

错误案例:

package main
import (
	"fmt"
	"sync" // 并发包
)

var wg sync.WaitGroup // 并发包的变量, 只定义无需初始化

//定义一个变量:
var cnt int

func iAdd(){
	defer wg.Done()
	for i := 1; i <= 10000; i++{
		cnt = cnt + 1
	}
}
func iSub(){
	defer wg.Done()
	for i := 1; i <= 10000; i++{
		cnt = cnt - 1
	}
}

func main(){   //主线程
	//开启一个协程--使用普通函数
	wg.Add(2)  // 协程数量+2
	go iAdd() // 开启协程
	go iSub()
	wg.Wait()

	fmt.Println(cnt) //结果:理论上cnt是0,但实际上不是
}

两个协程进行的顺序是不一定的,比如取cnt这个值时,两个协程进行的取值和运算操作顺序的关系,可能导致对原本正确的结果进行了覆盖,导致iAdd函数的偏移量和iSub函数的偏移量之和不等于0,实际结果是个不确定的值。

5.1 互斥锁 

正确案例:使用互斥锁同步协程

解决上面问题的方案:
确保一个协程在执行逻辑的时候另外的协程不执行---->利用锁的机制---->互斥锁

互斥锁:
Mutex为互斥锁,Lock()加锁,Unlock()解锁,使用Lock()加锁后,便不能再次对其进行加锁,直到利用Unlock()解锁对其解锁后,才能再次加锁,适用于读写不确定场景,即读写次数没有明显的区别-----性能效率相对来说偏低

package main
import (
	"fmt"
	"sync" // 并发包
)

var wg sync.WaitGroup // 并发包的变量, 只定义无需初始化
var lock sync.Mutex //加入互斥锁

//定义一个变量:
var cnt int

func iAdd(){
	defer wg.Done()
	for i := 1; i <= 10000; i++{
		lock.Lock() // 加锁
		cnt = cnt + 1
		lock.Unlock() // 解锁
	}
}
func iSub(){
	defer wg.Done()
	for i := 1; i <= 10000; i++{
		lock.Lock() // 加锁
		cnt = cnt - 1
		lock.Unlock() // 解锁

	}
}

func main(){   //主线程
	wg.Add(2) 
	go iAdd()
	go iSub()
	wg.Wait()

	fmt.Println(cnt) //结果:理论上cnt是0,但实际上不是
}


5.2 读写锁 

读写锁:

RWMutex是一个读写锁,其经常用于读次数远远多于写次数的场景。
在读的时候,数据之间不产生影响,写和读之间才会产生影响

package main
import (
	"fmt"
	"sync" // 并发包
	"time"
)

var wg sync.WaitGroup // 并发包的变量, 只定义无需初始化
var lock sync.RWMutex //加入读写锁

func read(){
	defer wg.Done()
	//如果只是读取数据,那么这个锁不产生任何影响,但是如果是写入数据,那么就会产生阻塞
	lock.RLock()
	fmt.Println("尝试读取数据中...")
	time.Sleep(time.Second)
	fmt.Println("读取数据成功!")
	lock.RUnlock()
}
func write(){
	defer wg.Done()
	lock.Lock()
	fmt.Println("尝试修改数据中...")
	time.Sleep(time.Second * 2)
	fmt.Println("修改数据成功!")
	lock.Unlock()
}

func main(){   //主线程
	//场景:读多写少
	wg.Add(1)
	go write()
	for i := 1; i < 20; i++{
		wg.Add(1)
		go read()
	}
	wg.Wait()
}


的过程中,锁生效,但是读可以并发读,锁没有影响

6、管道

6.1 管道概念

管道:

  • 管道本质就是一个数据结构--队列
  • 数据特性:先进先出
  • 自身线程安全,多协程访问时,不需要加锁,channel本身就是线程安全的
  • 管道有类型的,如:一个string类型的管道只能存放string类型数据

6.2 管道的定义

定义:

var 变量名 chan 数据类型

  • chan是管道关键字
  • 数据类型指的是管道的类型,指里面放入数据的类型,int类型的管道只能写入整数int
  • 管道是引用类型,必须初始化才能写入数据,即make后才能使用
package main
import (
	"fmt"
)

func main(){
	//定义管道、声明管道 ---> 定义一个int类型的管道
	var intChan chan int
	//通过make初始化:管道可以存放3个int类型的数据
	intChan = make(chan int,3)

	//证明管道是引用类型
	fmt.Printf("intChan的值:%v\n",intChan) //intChan的值:0xc0000ac080

	//向管道中写入数据
	intChan <- 10
	intChan <- 20
	//输出管道的长度
	fmt.Printf("管道的实际长度:%v,管道的容量:%v\n",len(intChan),cap(intChan)) // 管道的实际长度:2,管道的容量:3

	//从管道中读取数据
	num := <-intChan
	fmt.Println(num) // 10

	//关闭管道---> 不能向关闭的管道中写入数据
	close(intChan)
	// intChan <- 100 // 报错:panic: send on closed channel
	num,flag := <-intChan
	fmt.Println(num,flag) // 20 true
}

6.3 管道的关闭

管道的关闭:
使用内置函数close可以关闭管道,当管道关闭后,就不能再向管道写数据了,但是仍然可以从该管道读取数据。


例子:上面代码的

6.4 管道的遍历

管道的遍历:

管道支持for-range的方式进行遍历,注意:

  • 在遍历时,如果管道没有关闭,则会出现deadlock(死锁)的错误
  • 在遍历时,如果管道已经关闭,则会正常遍历数据,遍历完后,就会退出遍历 
package main
import (
	"fmt"
)

func main(){
	//定义管道、声明管道 ---> 定义一个int类型的管道
	var intChan chan int = make(chan int,100)

	for i := 1; i <= 100; i++{
		intChan <- i
	}
	//遍历前,如果没有关闭管道,那么会出现死锁 -- deadlock
	close(intChan)
	//for-range读取管道中的数据
	for v := range intChan{
		fmt.Printf("%v ",v)
	}
}

6.5 协程和管道协同工作案例

案例需求:

请完成协程和管道协同工作的案例,具体要求:

  1. 开启一个writeData协程,向管道中写入50个整数
  2. 开启一个readData协程,从管道中读取writeData写入的数据
  3. 注意:writeData和readData操作的是同一个管道
  4. 主线程需要等待writeData和readData协程都完成工作才能退出
package main
import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup // 并发包的变量, 只定义无需初始化

func writeData(intChan chan int){
	defer wg.Done() // 协程执行完毕,协程数量-1
	for i := 1; i <= 50; i++{
		fmt.Printf("写入数据:%v\n",i)
		intChan<- i		
		time.Sleep(time.Second * 1)
	}
	close(intChan)
}

func readData(intChan chan int){
	defer wg.Done() // 协程执行完毕,协程数量-1
	for v := range intChan{
		fmt.Printf("读取数据:%v\n",v)
		time.Sleep(time.Second * 1)
	}
}

func main(){
	//init
	var intChan chan int = make(chan int,100)
	//开启线程
	wg.Add(2)
	go writeData(intChan)
	go readData(intChan)
	wg.Wait()
}

6.6 声明只读只写管道

管道可以声明为只读或者只写性质:

package main

import (
	"fmt"
)

func main(){
	//默认情况下,管道是双向的 --> 可读可写
	var intChan1 chan int = make(chan int, 10)
	intChan1 <- 10
	fmt.Println(intChan1)

	//声明为只写 --> 只能写,不能读
	var intChan2 chan <- int = make(chan int, 5)
	intChan2 <- 10
	fmt.Println(intChan2)
	//fmt.Println(<- intChan2) // 报错:invalid operation: <-intChan2 (receive from send-only type chan<- int)

	//声明为只读--> 只能读,不能写
	var intChan3 <- chan int = make(chan int, 5)
	fmt.Println(intChan3)
	//intChan3 <- 10 // 报错:invalid operation: intChan3 <- 10 (send to receive-only type <-chan int)
}

6.7 管道的阻塞

阻塞的情况:

package main
import (
	"fmt"
	"sync"
	//"time"
)

var wg sync.WaitGroup // 并发包的变量, 只定义无需初始化

func writeData(intChan chan int){
	defer wg.Done() // 协程执行完毕,协程数量-1
	for i := 1; i <= 10; i++{
		fmt.Printf("写入数据:%v\n",i)
		intChan<- i		
		//time.Sleep(time.Second * 1)
	}
	close(intChan)
}

func readData(intChan chan int){
	defer wg.Done() // 协程执行完毕,协程数量-1
	for v := range intChan{
		fmt.Printf("读取数据:%v\n",v)
		//time.Sleep(time.Second * 1)
	}
}

func main(){
	//init
	var intChan chan int = make(chan int,5)
	//开启线程
	wg.Add(1)
	go writeData(intChan)
	//go readData(intChan)
	wg.Wait()
}
  • 在Go语言中,如果一个管道(channel)只被写入数据而没有被读取,那么最终会导致管道阻塞。这是因为Go语言中的管道是同步的,即写入操作会等待读取操作完成后才能继续如果没有读取操作来接收数据,写入操作就会一直等待,从而导致程序阻塞
  • 在代码片段中,如果writeData函数一直向intChan管道写入数据,而没有readData函数来读取这些数据,那么writeData函数中的intChan<- i操作会在管道缓冲区满后阻塞。如果缓冲区大小是5,那么在写入5个数据后,writeData函数就会阻塞,直到有读取操作来接收数据。
  • 如果程序中没有其他地方读取这个管道,那么writeData函数会一直阻塞,这可能会导致程序死锁。为了避免这种情况,通常应该确保在创建管道时,有相应的读取操作来处理写入的数据。
  • 在实际编程中,应该根据程序的逻辑和需求来设计管道的读写操作,确保数据能够被正确处理,避免出现阻塞或死锁的情况。如果确实需要一个只写而不读的管道,那么应该考虑使用其他机制来处理数据,或者在程序中添加适当的逻辑来处理这种情况。

确保有相应的读取操作(不管读写速度是否一致(例如读的速度小于写的速度),只要有读取操作就不会阻塞):

package main
import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup // 并发包的变量, 只定义无需初始化

func writeData(intChan chan int){
	defer wg.Done() // 协程执行完毕,协程数量-1
	for i := 1; i <= 10; i++{
		fmt.Printf("写入数据:%v\n",i)
		intChan<- i		
		time.Sleep(time.Second * 1)
	}
	close(intChan)
}

func readData(intChan chan int){
	defer wg.Done() // 协程执行完毕,协程数量-1
	for v := range intChan{
		fmt.Printf("读取数据:%v\n",v)
		time.Sleep(time.Second * 5)
	}
}

func main(){
	//init
	var intChan chan int = make(chan int,5)
	//开启线程
	wg.Add(2)
	go writeData(intChan)
	go readData(intChan)
	wg.Wait()
}

6.8 select功能

select功能:在Go语言中,select语句用于处理多个通道(channel)的操作。它类似于switch语句,但是专门用于处理通道的发送和接收操作select语句会监听多个通道的操作,一旦其中一个通道准备好进行发送或接收操作,select就会执行相应的分支。

在这个语法中,每个case分支对应一个通道操作。如果多个case分支同时满足条件,Go语言会随机选择一个分支执行如果没有任何分支满足条件,并且存在default分支,那么就会执行default分支。如果没有default分支,select语句会阻塞,直到有某个分支满足条件
 

  • case后面必须进行的是io操作,不能是等值,随机去选择一个io操作
  • default防止select被阻塞住,加入default
package main
import (
	"fmt"
	"time"
)


func main(){
	//chan int
	var intChan chan int = make(chan int, 5)
	go func(){
		time.Sleep(time.Second * 4)
		intChan <- 10
	}()
	//chan string
	var strChan chan string = make(chan string, 5)
	go func(){
		time.Sleep(time.Second * 2)
		strChan <- "hello world"
	}()
	//chan float32
	var floChan chan float32 = make(chan float32, 5)
	go func(){
		time.Sleep(time.Second * 1)
		floChan <- 1.1111
	}()

	//select--> 多路复用 --> 哪个管道有数据就执行哪个管道
	select{
		case v := <- intChan:
			fmt.Printf("select:读取intChan数据:%v\n",v)
		case v := <- strChan:
			fmt.Printf("select:读取strChan数据:%v\n",v)
		case <- floChan:
			fmt.Printf("floChan管道") //执行
		// default:
		// 	fmt.Println("防止select阻塞\n")
	}
}

6.9  defer + recover机制处理错误

问题原因:多个协程工作,其中一个协程出现panic,导致程序奔溃

解决方法:利用defer + recover捕获panic进行处理,即使协程出现问题,主线程仍然不受影响可以继续执行

package main
import (
	"fmt"
	"time"
)

//输出数字
func printNum(){
	for i := 1; i <= 10; i++{
		fmt.Println(i)
		//time.Sleep(time.Second * 1)
	}
}

//除法
func devide(){
	defer func(){
		err := recover()
		if err != nil{
			fmt.Println("devide()发生错误:",err)
		}
	}()
	num1 := 100000
	num2 := 0
	result := num1/num2
	fmt.Println(result)
}

func main(){
	go printNum()
	go devide()

	time.Sleep(time.Second * 5)
}

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

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

相关文章

C语言 | Leetcode C语言题解之第543题二叉树的直径

题目&#xff1a; 题解&#xff1a; typedef struct TreeNode Node;int method (Node* root, int* max) {if (root NULL) return 0;int left method (root->left, max);int right method (root->right, max);*max *max > (left right) ? *max : (left right);…

如何简化App Store提现?——作为游戏开发者的跨境收款体验分享

目录 如何简化App Store提现&#xff1f;——作为游戏开发者的跨境收款体验分享跨境收款常见的几个问题使用万里汇收款后的体验1. 结算流程简单&#xff0c;到账更快2. 多场景收付更灵活3. 多种支付方式支持 使用后的效果&#xff1a;资金管理更高效个人建议 如何简化App Store…

sql报错信息将字符串转换为 uniqueidentifier 时失败

报错信息&#xff1a; [42000] [Microsoft][SQL Server Native Client 10.0][SQL Server]将字符串转换为 uniqueidentifier 时失败 出错行如下&#xff1a; 表A.SourceCode 表B.ID 出错原因&#xff1a; SourceCode是nvarchar,但ID是uniqueidentifier 数据库查询字段和类…

【简信CRM-注册安全分析报告】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 暴力破解密码&#xff0c;造成用户信息泄露短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造成亏损无底洞…

DevOps业务价值流:架构设计最佳实践

系统设计阶段作为需求与研发之间的桥梁&#xff0c;在需求设计阶段的原型设计评审环节&#xff0c;尽管项目组人员可能未完全到齐&#xff0c;但关键角色必须到位&#xff0c;包括技术组长和测试组长。这一安排旨在同步推进两项核心任务&#xff1a;一是完成系统的架构设计&…

分享:文本转换工具:PDF转图片,WORD转PDF,WORD转图片

前言 鉴于网上大多数在线转换工具要么需要收费&#xff0c;要么免费后但转换质量极差的情况&#xff0c;本人开发并提供了PDF转图片&#xff0c;WORD转PDF&#xff0c;WORD转图片等的文本转换工具。 地址 http://8.134.236.93/entry/login 账号 账号&#xff1a;STAR001&a…

sublime可以写python吗

首先你需要安装一个Sublime Text&#xff08;http://www.sublimetext.com/&#xff09;和一个Python&#xff08;https://www.python.org/downloads/&#xff09;&#xff0c; 接下来打开Sublime Text&#xff1a; 1、如下图所示&#xff0c;点击菜单栏中的Tools —> Buil…

聊一聊Elasticsearch的基本原理与形成机制

1、搜索引擎的基本原理 通常搜索引擎包括&#xff1a;数据采集、文本分析、索引存储、搜索等模块&#xff0c;它们之间的协作流程如下图&#xff1a; 数据采集模块负责采集需要搜索的数据源。 文本分析模块是将结构化数据中的长文本切分成有实际意义的词&#xff0c;这样用户…

IO流篇(一、File)

目录 一、学习前言 二、文件简介 三、文件使用 1. 绝对路径 vs 相对路径 2. 路径分隔符 3. 属性&#xff08;字段&#xff09; 4. 构造方法 5. 常用方法 5.1. 获取文件的相关信息 5.2. 判断功能 5.3. 新建和删除 5.4. 文件的获取 5.5. 重命名文件 四、文件使用练习…

【部署glm4】属性找不到、参数错误问题解决(思路:修改模型包版本)

前言&#xff1a;在部署glm时&#xff0c;遇到了一些属性找不到、参数错误的问题&#xff0c;通常遇到这种问题都是因为模型包版本问题导致的&#xff0c;要注意模型版本是否可用。 【运行官方vllm_cli_demo.py】报错 GLM-4: [rank0]: Traceback (most recent call last): [ran…

全双工通信协议WebSocket——使用WebSocket实现智能学习助手/聊天室功能

一.什么是WebSocket&#xff1f; WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器的全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传输 HTTP 协议是一种无状态的、无连接的、单向的应用…

【Linux】冯诺依曼体系、再谈操作系统

目录 一、冯诺依曼体系结构&#xff1a; 1、产生&#xff1a; 2、介绍&#xff1a; 二、再谈操作系统&#xff1a; 1、为什么要管理软硬件资源&#xff1a; 2、操作系统如何进行管理&#xff1a; 3、库函数&#xff1a; 4、学习操作系统的意义&#xff1a; 一、冯诺依曼…

Linux的目录结构 | 命令的认识 | 相对路径 | 绝对路径 | 常用命令(一)

文章目录 1.Linux的目录结构2.命令的认识3.相对路径和绝对路径4.常用命令&#xff08;目录文件操作&#xff09;5.常用命令&#xff08;文本查看&#xff09; 1.Linux的目录结构 \ &#xff1a;根目录 root&#xff1a;root用户的工作目录 home&#xff1a;普通用户的工作目录 …

linux nvidia/cuda安装

1.查看显卡型号 lspci |grep -i vga2.nvidia安装 2.1在线安装 终端输入&#xff08;当显卡插上之后&#xff0c;系统会有推荐的安装版本&#xff09; ubuntu-drivers devices可得到如下内容 vendor : NVIDIA Corporation model : TU104GL [Tesla T4] driver : nvid…

简单又便宜的实现电脑远程开机唤醒方法

现有的远程开机方案 1&#xff09;使用向日葵开机棒 缺点是比较贵一点&#xff0c;开机棒要一百多&#xff0c;而且查了评论发现挺多差评说不稳定&#xff0c;会有断联和无法唤醒的情况&#xff0c;而且设置也麻烦&#xff0c;还需要网卡支持WOL 2&#xff09;使用远程开机卡 …

容器架构-Docker的成长之路

目录 1. 什么是容器 2. 容器 vs 虚拟机 3. Docker极速上手指南 环境准备 3.1 配置docker源 3.2 下载镜像加速的配置 3.3 下载自动补全工具 4. Docker C/S架构 5. Docker的镜像管理 5.1 下载nginx:alpine镜像并查看 5.2 sl大法 5.3 删除镜像 5.4 镜像清理用的命令 5…

【开源社区】ELK 磁盘异常占用解决及优化实践

1、问题及场景描述 本文主要讨论在 CentOS环境下基于 rpm 包部署 ELK 系统磁盘异常占用的问题解析和解决方案。 生产问题描述&#xff1a;以下问题现实场景基于ELK体系下&#xff0c;ES服务的磁盘占用问题解析。默认情况下&#xff0c;基于 RPM 安装的 Elasticsearch 服务的安…

仪表板展示|DataEase看中国:历年双十一电商销售数据分析

背景介绍 2024年“双十一”购物季正在火热进行中。自2009年首次推出至今&#xff0c;“双十一”已经成为中国乃至全球最大的购物狂欢节&#xff0c;并且延伸到了全球范围内的电子商务平台。随着人们消费水平的提升以及电子商务的普及&#xff0c;线上销售模式也逐渐呈现多元化…

【深度学习】论文笔记:空间变换网络(Spatial Transformer Networks)

博主简介&#xff1a;努力学习的22级计算机科学与技术本科生一枚&#x1f338;博主主页&#xff1a; Yaoyao2024往期回顾&#xff1a; 【机器学习】有监督学习由浅入深讲解分类算法Fisher算法讲解每日一言&#x1f33c;: 今天不想跑&#xff0c;所以才去跑&#xff0c;这才是长…

基于java+SpringBoot+Vue的旅游管理系统设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…