GO语言基础笔记(八):高级特性与性能优化

        

目录

反射(Reflection)

反射概念

反射的关键概念

反射的常见用途

代码示例

1. 检查类型和值

2. 修改变量值

3. 调用函数

4. 结构体反射

并发模式(Concurrency Patterns)

1. Worker Pool 模式

工作原理

在代码中的体现

2. Pipeline 模式

工作原理

在代码中的体现

3. Fan-in/Fan-out 模式

工作原理

在代码中的体现

性能优化(Performance Optimization)

1. 合理的设计和算法选择

2. 使用性能分析工具

实践:优化已有代码

第一步:性能分析

第二步:优化目标和策略

优化后的代码

第三步:性能测试


        学习反射、并发模式和性能优化。 实践:优化已有代码,实践性能分析工具。

反射(Reflection)

  • 反射是 Go 语言中一个强大且复杂的特性,它允许程序在运行时检查变量的类型和值,甚至修改它们。反射主要通过 reflect 包来实现。
  • 反射的使用场景包括:检查函数参数类型,实现泛型处理结构,或在不了解接口具体类型的情况下,动态调用其方法。
  • 使用反射时需注意其对性能的影响,因为反射操作通常比直接的类型断言或类型检查要慢。

反射概念

反射是 Go 语言的一个高级特性,它允许程序在运行时动态地检查变量的类型(Type)和值(Value)。这在 Go 语言中通过 reflect 包实现。

反射的关键概念

  1. Type:反映了变量的类型。
  2. Value:反映了变量的值。
  3. Kind:表示基础类型,如 intfloatstruct 等。

反射的常见用途

  • 检查函数参数的类型。
  • 实现泛型处理结构。
  • 在不知道接口具体类型的情况下,动态调用其方法。

代码示例

        让我们通过一个简单的例子来理解反射的工作原理。假设我们有一个函数,它接收一个空接口类型的参数,并使用反射来检查该参数的类型和值。

package mainimport ("fmt""reflect"
)func reflectTypeAndValue(x interface{}) {t := reflect.TypeOf(x)v := reflect.ValueOf(x)fmt.Printf("Type: %v, Value: %v\n", t, v)
}func main() {var a int = 42var b string = "GoLang"reflectTypeAndValue(a)reflectTypeAndValue(b)
}

1. 检查类型和值

        反射可以用来动态地检查变量的类型和值。

package mainimport ("fmt""reflect"
)func main() {x := 3.4fmt.Println("type:", reflect.TypeOf(x))v := reflect.ValueOf(x)fmt.Println("value:", v)fmt.Println("type:", v.Type())fmt.Println("kind:", v.Kind())fmt.Println("value:", v.Float())
}

2. 修改变量值

        使用反射,你可以修改变量的值,前提是这个值是可设置的(settable)

package mainimport ("fmt""reflect"
)func main() {x := 3.4v := reflect.ValueOf(&x)fmt.Println("settability of v:", v.CanSet())v = v.Elem()fmt.Println("The Elem of v is: ", v)fmt.Println("settability of v:", v.CanSet())v.SetFloat(7.1)fmt.Println(v.Interface())fmt.Println(x)
}

3. 调用函数

        使用反射,你可以动态调用函数

package mainimport ("fmt""reflect"
)func Add(a, b int) int {return a + b
}func main() {funcValue := reflect.ValueOf(Add)params := []reflect.Value{reflect.ValueOf(10),reflect.ValueOf(20),}results := funcValue.Call(params)fmt.Println(results[0].Int()) // 输出结果: 30
}

4. 结构体反射

        你可以通过反射来动态访问和修改结构体的字段。

package mainimport ("fmt""reflect"
)type MyStruct struct {Field1 intField2 string
}func main() {s := MyStruct{Field1: 10, Field2: "Hello"}sValue := reflect.ValueOf(&s).Elem()for i := 0; i < sValue.NumField(); i++ {fmt.Println(sValue.Field(i))}// 修改字段的值if sValue.Field(0).CanSet() {sValue.Field(0).SetInt(20)}if sValue.Field(1).CanSet() {sValue.Field(1).SetString("World")}fmt.Println("After modification:", s)
}

        在这些示例中,我们演示了如何使用反射来检查类型和值、修改变量值、动态调用函数和处理结构体字段。反射是一个非常强大的工具,但它也可能使代码变得复杂且难以理解。因此,它应该在确有必要的情况下谨慎使用。

并发模式(Concurrency Patterns)

  • Go 语言的并发是其核心特性之一。通过 Goroutines 和 Channels,Go 可以有效地实现并发编程。
  • 掌握不同的并发模式,如 Worker Pool 模式、Pipeline 模式和 Fan-in/Fan-out 模式,对于构建高效且可靠的并发程序至关重要。
  • 正确使用并发可以显著提高程序性能,但同时也要注意避免常见的并发问题,如死锁、竞态条件和资源竞争

1. Worker Pool 模式

        在 Worker Pool 模式中,一组固定数量的工作协程(workers)被创建来处理任务队列中的任务。

package mainimport ("fmt""sync""time"
)// worker 函数代表一个工作协程,它从 jobs 通道接收任务,并将处理结果发送到 results 通道。
func worker(id int, jobs <-chan int, results chan<- int) {for j := range jobs {fmt.Println("worker", id, "started job", j)time.Sleep(time.Second) // 模拟耗时操作fmt.Println("worker", id, "finished job", j)results <- j * 2 // 发送处理结果到 results 通道}
}func main() {const numJobs = 5jobs := make(chan int, numJobs)results := make(chan int, numJobs)// 创建 3 个工作协程for w := 1; w <= 3; w++ {go worker(w, jobs, results)}// 分发任务到 jobs 通道for j := 1; j <= numJobs; j++ {jobs <- j}close(jobs) // 关闭 jobs 通道,表示不再有新的任务// 收集所有工作协程的处理结果for a := 1; a <= numJobs; a++ {<-results}
}
工作原理
  • 任务分配:在 Worker Pool 模式中,创建一定数量的工作协程(workers)。这些协程通常从一个共享的任务队列中获取任务。任务队列是一个 Channel,用于存储待处理的工作。
  • 任务处理:每个工作协程从任务队列(jobs Channel)中获取任务,并开始处理。处理完成后,可能会将结果发送到另一个结果队列(results Channel)。
  • 同步机制:主协程通常等待所有工作协程完成任务。这可以通过关闭任务队列的 Channel 并使用 sync.WaitGroup 或接收所有结果来实现。
在代码中的体现
  • jobs Channel 用于传递任务给工作协程。
  • results Channel 用于收集工作结果。
  • 每个 worker 函数在循环中从 jobs Channel 接收任务,处理完成后将结果发送到 results Channel。
  • 主函数中,所有任务被发送到 jobs Channel,然后该 Channel 被关闭,表示没有更多的任务。
  • 主函数等待从 results Channel 接收所有结果,确保所有任务都被处理。

2. Pipeline 模式

        Pipeline 模式涉及将一系列处理步骤连接在一起,每个步骤由一个 Goroutine 处理,并通过 Channels 进行通信。

package mainimport ("fmt"
)// gen 函数将一个整数列表转换为一个通道,用于发送这些整数。
func gen(nums ...int) <-chan int {out := make(chan int)go func() {for _, n := range nums {out <- n}close(out) // 发送完毕后关闭通道}()return out
}// sq 函数对从输入通道接收到的每个整数求平方,并将结果发送到输出通道。
func sq(in <-chan int) <-chan int {out := make(chan int)go func() {for n := range in {out <- n * n}close(out) // 处理完毕后关闭通道}()return out
}func main() {// 设置 pipeline:gen -> sq -> sq// 第一个 sq 处理 gen 的输出,第二个 sq 处理第一个 sq 的输出for n := range sq(sq(gen(2, 3))) {fmt.Println(n) // 输出结果}
}
工作原理
  • 阶段性处理:Pipeline 模式将数据处理过程分为多个阶段,每个阶段由一个协程处理,并通过 Channel 连接。
  • 连续传递:每个阶段的协程从输入 Channel 读取数据,进行处理,然后将结果写入输出 Channel。下一个阶段的协程将从这个输出 Channel 读取数据,如此循环。
  • 数据流动:这种模式允许数据在多个处理步骤中连续流动,类似于工业生产中的流水线。
在代码中的体现
  • gen 函数初始化数据流,将整数发送到输出 Channel。
  • sq 函数接收整数,计算其平方,然后将结果发送到另一个 Channel。
  • 主函数中,通过连续调用 sq 函数形成 pipeline。每个 sq 函数都是 pipeline 的一个阶段。

3. Fan-in/Fan-out 模式

        在 Fan-out 模式中,多个 Goroutines 从同一个 Channel 读取数据,分担工作负载。在 Fan-in 模式中,多个 Goroutines 写入同一个 Channel,汇集它们的结果。

package mainimport ("fmt""sync"
)// produce 函数生产一系列整数,发送到通道 ch。
func produce(ch chan<- int, id int) {for i := 0; i < 5; i++ {ch <- ifmt.Printf("Producer %d produced %d\n", id, i)}close(ch) // 生产完毕后关闭通道
}// consume 函数从通道 ch 消费整数。
func consume(ch <-chan int, id int) {for n := range ch {fmt.Printf("Consumer %d consumed %d\n", id, n)}
}func main() {ch := make(chan int)// 启动单个生产者协程go produce(ch, 1)var wg sync.WaitGroupconst numConsumers = 3wg.Add(numConsumers) // 设置 WaitGroup 的计数// 启动多个消费者协程(Fan-out)for i := 0; i < numConsumers; i++ {go func(id int) {defer wg.Done() // 协程完成时减少 WaitGroup 计数consume(ch, id)}(i + 1)}wg.Wait() // 等待所有消费者协程完成
}
工作原理
  • Fan-out:多个协程从同一个输入 Channel 读取数据,分担工作负载。这种方式提高了数据处理的并行度。
  • Fan-in:多个协程将结果写入同一个输出 Channel,将多个数据源的数据汇聚在一起。
在代码中的体现
  • produce 函数中,生产者协程生成数据并发送到 Channel。
  • 多个 consume 函数(消费者协程)从同一个 Channel 接收数据并处理(Fan-out)。
  • 使用 sync.WaitGroup 确保所有消费者协程完成工作。

        这些并发模式在 Go 语言中非常有用,它们可以帮助您构建高效且可靠的并发程序。每种模式都有其特定的使用场景和优势,选择合适的模式取决于您的具体需求和应用场景。

性能优化(Performance Optimization)

  • 性能优化是提升 Go 程序效率的关键步骤。首先,应当通过合理的设计和算法选择来确保程序基础的高效性。
  • 使用性能分析工具,如 pprof,可以帮助您找出程序中的性能瓶颈。pprof 支持 CPU 分析、内存分析等,让您可以深入了解程序的运行细节。
  • 除了标准的性能优化工具外,还应注意代码层面的优化,例如减少不必要的内存分配、优化循环和数据结构选择等。

1. 合理的设计和算法选择

        优化的第一步是选择合适的算法和数据结构。高效的算法和数据结构可以减少不必要的计算和内存使用。

package mainimport "fmt"// 使用快速排序而非冒泡排序来提高排序效率
func quickSort(a []int) []int {if len(a) < 2 {return a}left, right := 0, len(a)-1pivot := a[(left+right)/2]a[(left+right)/2], a[right] = a[right], a[(left+right)/2]for i := range a {if a[i] < pivot {a[left], a[i] = a[i], a[left]left++}}a[left], a[right] = a[right], a[left]quickSort(a[:left])quickSort(a[left+1:])return a
}func main() {a := []int{3, 6, 8, 10, 1, 2, 1}fmt.Println(quickSort(a))
}

2. 使用性能分析工具

  pprof 是 Go 语言中一个强大的性能分析工具,用于识别程序中的性能瓶颈。

package mainimport ("log""net/http""_net/http/pprof" // 添加 pprof
)func main() {go func() {log.Println(http.ListenAndServe("localhost:6060", nil))}()// 此处为程序的主逻辑
}

 

实践:优化已有代码

  • 选取一个实际的 Go 项目,运用以上知识对其进行优化。
  • 首先运行性能分析,确定优化目标。然后,根据分析结果调整代码,可能涉及到重构数据结构、改进算法逻辑或调整并发模式。
  • 优化后,再次进行性能测试,比较优化前后的差异,以确保所做更改有效果

        假设我们有一个 Go 编写的简单 Web 服务,它处理一些计算密集型任务和数据处理任务。 

        

package mainimport ("fmt""net/http""time"
)func handler(w http.ResponseWriter, r *http.Request) {start := time.Now()result := heavyComputation() // 假设这是一个计算密集型操作duration := time.Since(start)fmt.Fprintf(w, "Result: %v, took: %v", result, duration)
}func heavyComputation() int {time.Sleep(100 * time.Millisecond) // 模拟重计算return 42
}func main() {http.HandleFunc("/", handler)http.ListenAndServe(":8080", nil)
}

 

第一步:性能分析
  1. 使用 pprof 对服务进行性能分析。
  2. 添加 pprof 相关代码到项目中,并在运行时收集性能数据。
第二步:优化目标和策略

假设我们发现 heavyComputation 函数是性能瓶颈。 优化策略可能包括:

  1. 改进算法:如果可能,寻找更高效的算法来替代现有逻辑。
  2. 并发执行:如果该函数可以并行处理,考虑使用 Goroutines 来分配任务。
  3. 缓存结果:如果函数输出对于相同的输入是不变的,考虑使用缓存来避免重复计算。
优化后的代码

假设我们决定实现一个简单的缓存机制。

package mainimport ("fmt""net/http""sync""time"
)var (cache   = make(map[int]int)cacheMu sync.Mutex
)func handler(w http.ResponseWriter, r *http.Request) {start := time.Now()result := cachedComputation(42) // 使用缓存duration := time.Since(start)fmt.Fprintf(w, "Result: %v, took: %v", result, duration)
}func cachedComputation(input int) int {cacheMu.Lock()if val, ok := cache[input]; ok {cacheMu.Unlock()return val}cacheMu.Unlock()val := heavyComputation() // 原始计算cacheMu.Lock()cache[input] = valcacheMu.Unlock()return val
}func heavyComputation() int {time.Sleep(100 * time.Millisecond) // 模拟重计算return 42
}func main() {http.HandleFunc("/", handler)http.ListenAndServe(":8080", nil)
}

 

第三步:性能测试
  1. 再次运行 pprof 性能分析,比较优化前后的性能。
  2. 检查响应时间是否有所改善,以及 CPU 和内存使用情况是否有所优化。

        这个例子展示了基本的优化流程,但实际项目中的优化可能更复杂。它可能涉及到深入的算法分析,复杂的并发模式调整,或底层数据结构的重构。始终记住,优化应该基于性能分析的数据,而非随意猜测。同时,优化时应考虑代码的可读性和维护性。 

pprof

  pprof 是 Go 语言提供的性能分析工具,它能够帮助开发者分析程序的 CPU 使用情况和内存分配。以下是如何在 Go 程序中使用 pprof 的示例:

使用

步骤 1: 导入 pprof

        首先,在你的 Go 程序中导入 net/http/pprof 包。这将自动注册 HTTP 服务器的多个 pprof 路由。

import ("net/http"_ "net/http/pprof" // 注意这里使用了空白导入语法
)

步骤 2: 启动 HTTP 服务器

        启动一个 HTTP 服务器以便可以通过 HTTP 请求访问 pprof 数据。通常,这可以作为程序的一部分在一个新的 goroutine 中进行。

go func() {log.Println(http.ListenAndServe("localhost:6060", nil))
}()

步骤 3: 收集性能数据

一旦你的程序开始运行,你可以通过访问特定的 URL 来收集性能数据。例如:

  • 访问 http://localhost:6060/debug/pprof/ 可查看所有可用的 pprof 信息。
  • 访问 http://localhost:6060/debug/pprof/heap 可获取内存分配的情况。
  • 访问 http://localhost:6060/debug/pprof/profile?seconds=30 可进行 30 秒的 CPU 分析。

步骤 4: 使用 go tool pprof 分析数据

        你可以使用 go tool pprof 命令来下载和分析这些数据。例如,要分析 CPU 使用情况,可以使用以下命令:

go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30

        在 pprof 的交互模式下,你可以使用各种命令来查看和分析性能数据,如 top 查看消耗最多 CPU 的函数,或 web 生成函数调用图。

  1. top: 这个命令显示消耗最多 CPU 时间的函数。它提供了一个函数列表,按照其在 CPU 使用上的占比排序。

    示例:

    scssCopy code

    (pprof) top

  2. web: 这个命令生成一个 SVG 格式的函数调用图,并在默认的网页浏览器中打开它。这个图表展示了函数之间的调用关系以及它们的 CPU 使用情况。

    示例:

    scssCopy code

    (pprof) web

  3. list: 这个命令用于显示指定函数的源代码,并标注每一行的 CPU 使用情况。这对于深入理解特定函数的性能特性非常有用。

    示例:

    scssCopy code

    (pprof) list [函数名]

  4. tree: 这个命令以树状格式显示调用图,帮助你理解函数调用的层次结构。

    示例:

    scssCopy code

    (pprof) tree

  5. peek: 这个命令用于查看与特定项相关的所有路径,帮助你理解特定函数或路径是如何贡献到总体资源使用的。

    示例:

    scssCopy code

    (pprof) peek [函数名或路径]

  6. text: 以纯文本格式显示 top 命令类似的输出,但可以更容易地用于报告或进一步分析。

    示例:

    scssCopy code

    (pprof) text

  7. exit 或 quit: 退出 pprof 的交互模式。

    示例:

    bashCopy code

    (pprof) exit

例子

        访问:

http://localhost:6060/debug/pprof/

/debug/pprof/Types of profiles available:
Count	Profile
8	allocs
0	block
0	cmdline
6	goroutine
8	heap
0	mutex
0	profile
7	threadcreate
0	trace
full goroutine stack dump
Profile Descriptions:allocs: A sampling of all past memory allocations
block: Stack traces that led to blocking on synchronization primitives
cmdline: The command line invocation of the current program
goroutine: Stack traces of all current goroutines
heap: A sampling of memory allocations of live objects. You can specify the gc GET parameter to run GC before taking the heap sample.
mutex: Stack traces of holders of contended mutexes
profile: CPU profile. You can specify the duration in the seconds GET parameter. After you get the profile file, use the go tool pprof command to investigate the profile.
threadcreate: Stack traces that led to the creation of new OS threads
trace: A trace of execution of the current program. You can specify the duration in the seconds GET parameter. After you get the trace file, use the go tool trace command to investigate the trace.

        Go 语言的 pprof 分析工具中,/debug/pprof/ 路径提供了访问多种性能分析数据的接口。每种类型的性能数据都提供了对程序运行的不同视角。下面是您列出的每种分析数据类型的解释:

  1. allocs: 这个配置文件提供了所有过去内存分配的采样。它有助于理解内存分配的模式和频率。

  2. block: 这个配置文件显示了导致同步原语上阻塞的堆栈跟踪,例如在通道操作或互斥锁等待上的阻塞。它对于理解并发性能问题非常有用。

  3. cmdline: 这个配置文件显示了当前程序的命令行调用方式,即启动程序时使用的命令和参数。

  4. goroutine: 提供了当前所有 goroutine 的堆栈跟踪。这对于理解程序的并发行为和寻找死锁等问题非常有价值。

  5. heap: 提供了活跃对象的内存分配的采样。通过设置 gc GET 参数,可以在采样之前触发垃圾回收。这有助于分析内存使用情况和潜在的内存泄露。

  6. mutex: 显示持有争用互斥锁的堆栈跟踪。它有助于识别可能导致性能问题的锁争用。

  7. profile: 提供 CPU 的使用情况。您可以通过设置 seconds GET 参数来指定分析持续的时间。分析完成后,可以使用 go tool pprof 命令来分析 CPU 配置文件。

  8. threadcreate: 显示导致创建新操作系统线程的堆栈跟踪。这可以帮助您了解线程创建的模式和原因。

  9. trace: 提供了当前程序的执行追踪。可以通过设置 seconds GET 参数来指定追踪的持续时间。追踪完成后,可以使用 go tool trace 命令来分析追踪文件。

每个配置文件提供了程序性能和行为的不同视角,帮助开发者更全面地理解和优化其 Go 程序。通过使用这些工具,您可以诊断各种性能问题,比如内存泄露、CPU 瓶颈、锁争用等。

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

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

相关文章

Unity坦克大战开发全流程——结束场景——通关界面

结束场景——通关界面 就照着这样来拼 写代码 hideme不要忘了 修改上一节课中的代码

动态内存管理篇

为什么要动态内存分配&#xff1f; 之前&#xff0c;我们向内存申请空间&#xff0c;有两种方式&#xff0c;一种是定义变量&#xff0c;一种是建立数组&#xff1b;但是&#xff0c;这两种方式都有缺陷&#xff0c;创建的空间大小是固定的&#xff0c;在程序的运行过程中&…

买对好车省钱又防坑,高性价比的买车攻略

一、教程描述 正所谓隔行如隔山&#xff0c;买车这件事情并不简单&#xff0c;买车的内幕还是有不少的&#xff0c;本套教程讲述买车攻略&#xff0c;非常适合准备买车的朋友&#xff0c;可以帮助大家买车少入坑&#xff0c;高性价比买到自己心仪的车。本套买车教程&#xff0…

2023-12-23 LeetCode每日一题(移除石子使总数最小)

2023-12-23每日一题 一、题目编号 1962. 移除石子使总数最小二、题目链接 点击跳转到题目位置 三、题目描述 给你一个整数数组 piles &#xff0c;数组 下标从 0 开始 &#xff0c;其中 piles[i] 表示第 i 堆石子中的石子数量。另给你一个整数 k &#xff0c;请你执行下述…

【随口一说】最近的CSDN

这段时间随便发的一篇博文很快就有“点赞”、“收藏”、“关注”的信息&#xff0c; 而且简单看了一眼用户&#xff0c;很多都是空的或者一堆转载&#xff0c; 机器人也太明显了点&#xff0c;很让人不舒服&#xff0c; 不花点心思设计文章评优推送算法反倒用机器人刷热门&…

电机(一):直流有刷电机和舵机

声明&#xff1a;以下图片来自于正点原子&#xff0c;仅做学习笔记使用 电机专题&#xff1a; 直流电机&#xff1a;直流有刷BDC&#xff08;内含电刷&#xff09;&#xff0c;直流无刷BLDC&#xff08;大疆的M3508和M2006&#xff09;,无刷电机有以下三种形式&#xff1a;&a…

C语言之分支与循环【附6个练习】

文章目录 前言一、什么是语句&#xff1f;1.1 表达式语句1.2 函数调用语句1.3 控制语句1.4 复合语句1.5 空语句 二、分支语句&#xff08;选择结构&#xff09;2.1 if语句2.1.1 悬空else2.1.2 练习&#xff08;1. 判断一个数是否为奇数 2. 输出1-100之间的奇数&#xff09; 2.2…

deepfacelive实时换脸教程(2024最新版)

deepfacelive其实操作用法很简单&#xff0c;难的是模型的制作。本帖主要讲deepfacelive&#xff08;下文简称dflive&#xff09;软件本身的操作&#xff0c;以及模型怎么从dfl转格式过来&#xff0c;至于模型如何训练才能效果好&#xff0c;请移步教程区&#xff0c;看deepfac…

51单片机中TCON, IE, PCON等寄存器的剖析

在单片机中&#xff0c;如何快速通过名字记忆IQ寄存器中每一个控制位的作用呢&#xff1f; IE&#xff08;interrupt enable&#xff09;寄存器中&#xff0c;都是中断的使能位置。 其中的EA&#xff08;enable all&#xff09;是总使能位&#xff0c;ES(enable serial)是串口…

构建安全的SSH服务体系

某公司的电子商务站点由专门的网站管理员进行配置和维护&#xff0c;并需要随时从Internet进行远程管理&#xff0c;考虑到易用性和灵活性&#xff0c;在Web服务器上启用OpenSSH服务&#xff0c;同时基于安全性考虑&#xff0c;需要对 SSH登录进行严格的控制&#xff0c;如图10…

WorkQueue模型

WorkQueues&#xff0c;也被称为任务队列模型。当消息处理比较耗时的时候&#xff0c;可能生产消息的速度会远远大于消息的消费速度。长此以往&#xff0c;消息就会堆积越来越多&#xff0c;无法及时的处理。此时就可以使用work模型&#xff1a;让多个消费者绑定到一个队列&…

gem5学习(8):创建一个简单的缓存对象--Creating a simple cache object

目录 一、SimpleCache SimObject 二、Implementing the SimpleCache 1、getSlavePort() 2、handleRequest() 3、AccessEvent() 4、accessTiming() &#xff08;1&#xff09;缓存命中&#xff1a;sendResponse() &#xff08;2&#xff09;缓存未命中&#xff1a; 三、…

matlab概率论例子

高斯概率模型&#xff1a; [f,xi] ksdensity(x): returns a probability density estimate, f, for the sample in the vector x. The estimate is based on a normal kernel function, and is evaluated at 100 equally spaced points, xi, that cover the range of the da…

Mybatis行为配置之Ⅰ—缓存

专栏精选 引入Mybatis Mybatis的快速入门 Mybatis的增删改查扩展功能说明 mapper映射的参数和结果 Mybatis复杂类型的结果映射 Mybatis基于注解的结果映射 Mybatis枚举类型处理和类型处理器 再谈动态SQL Mybatis配置入门 Mybatis行为配置之Ⅰ—缓存 Mybatis行为配置…

读书笔记1-C++ Primer Plus

C是在C语言基础上开发的一种集面向对象编程&#xff08;OOP&#xff09;、通用编程和传统的过程化编程于一体的编程语言。本书是根据2003年的ISO/ANSI C标准编写的&#xff0c;通过大量短小精悍的程序详细而全面地阐述了C的基本概念和技术。 全书分17章和10个附录&#xff0c;分…

使用WAZUH检测LD_PRELAOD劫持、SQL注入、主动响应防御

目录 1、检查后门 使用工具检测后门 1.chkrootkit 2.rkhunter 手动检查文件 检查ld.so.preload文件 2、检测LD_PRELOAD ubuntu配置 wazuh配置 3、检测SQL注入 ubuntu配置 攻击模拟 4、主动响应 wauzh的安装以及设置代理可以参考本篇&#xff1a;WAZUH的安装、设置…

Apache Flink连载(二十三):Flink HA - Flink基于Yarn HA

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Yarn HA配置 ​​​​…

Cache替换算法

由于Cache很小&#xff0c;主存很大&#xff0c;Cache很容易装满&#xff0c;Cache满了怎么办&#xff1f; ——采用替换算法。 全相联映射&#xff1a;Cache完全满了才需要替换&#xff0c;需要在全局中选择替换哪一块。直接映射&#xff1a;如果对应位置非空&#xff0c;则…

linux线程与进程

简要 在Linux系统中&#xff0c;进程&#xff08;Process&#xff09;和线程&#xff08;Thread&#xff09;是操作系统中两个重要的概念&#xff0c;它们都是用于执行程序的执行单元&#xff0c;但有一些关键的区别。 在Linux系统中&#xff0c;可以使用fork系统调用创建新…

Vue3-30-路由-嵌套路由的基本使用

什么是嵌套路由 嵌套路由 &#xff1a;就是一个组件内部还希望展示其他的组件&#xff0c;使用嵌套的方式实现页面组件的渲染。 就像 根组件 通过路由渲染 普通组件一样&#xff0c;嵌套路由也是一样的道理。 嵌套路由的相关关键配置 1、<router-view> 标签 声明 被嵌套组…