首次提交:初始化项目代码
This commit is contained in:
Generated
+8
@@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
Generated
+1
@@ -0,0 +1 @@
|
||||
bulma
|
||||
Generated
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="com.codeverse.userSettings.MarscodeWorkspaceAppSettingsState">
|
||||
<option name="chatAppRouterInfo" value="chat-session" />
|
||||
<option name="progress" value="1.0" />
|
||||
</component>
|
||||
</project>
|
||||
Generated
+9
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="Go" enabled="true" />
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/bulma.iml" filepath="$PROJECT_DIR$/.idea/bulma.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Generated
+6
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,36 @@
|
||||
// bulma/atomic_config.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config 配置结构体(实际项目中可扩展)
|
||||
type Config struct {
|
||||
APIURL string // API地址
|
||||
TimeoutMS int // 超时时间(毫秒)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var current atomic.Value
|
||||
// 初始化配置:首次存储需指定类型(推荐存储指针,避免值拷贝)
|
||||
current.Store(&Config{APIURL: "v1.example.com", TimeoutMS: 1000})
|
||||
|
||||
// 后台goroutine:模拟配置热更新(实际场景可从配置中心拉取)
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
newConf := &Config{APIURL: "v2.example.com", TimeoutMS: 2000}
|
||||
current.Store(newConf) // 原子替换整个配置
|
||||
fmt.Println("🔄 配置已热更新!")
|
||||
}()
|
||||
|
||||
// 业务goroutine:持续读取配置(无锁,高效)
|
||||
for i := 0; i < 5; i++ {
|
||||
// 类型断言:需与存储类型一致(此处为*Config),否则panic
|
||||
cfg := current.Load().(*Config)
|
||||
fmt.Printf("[%d] 当前API: %s, 超时: %dms\n", i, cfg.APIURL, cfg.TimeoutMS)
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// bulma/atomic_counter.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var atomicCounter atomic.Int64 // 原子计数器(新类型)
|
||||
var muCounter int64 // Mutex保护的计数器
|
||||
var mu sync.Mutex // 互斥锁
|
||||
|
||||
// 原子操作计数
|
||||
start := time.Now()
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 1000; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 1000; j++ {
|
||||
atomicCounter.Add(1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
atomicTime := time.Since(start)
|
||||
|
||||
// Mutex 计数(对比)
|
||||
start = time.Now()
|
||||
for i := 0; i < 1000; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 1000; j++ {
|
||||
mu.Lock()
|
||||
muCounter++
|
||||
mu.Unlock()
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
mutexTime := time.Since(start)
|
||||
|
||||
fmt.Printf("✅ 原子计数: %d (耗时: %v)\n", atomicCounter.Load(), atomicTime)
|
||||
fmt.Printf("✅ Mutex计数: %d (耗时: %v)\n", muCounter, mutexTime)
|
||||
fmt.Printf("🚀 性能提升: %.1fx\n", float64(mutexTime)/float64(atomicTime))
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
// bulma/atomic_flag.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var ready atomic.Bool // 就绪标志(原子布尔值)
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// 消费者goroutine:等待标志位变为true后执行
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// 循环检测标志位,直到变为true(无锁,高效)
|
||||
for !ready.Load() {
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
fmt.Println("✅ 消费者检测到 ready=true,开始处理")
|
||||
}()
|
||||
|
||||
// 生产者goroutine:模拟初始化操作,完成后设置标志位
|
||||
time.Sleep(50 * time.Millisecond) // 模拟初始化耗时
|
||||
ready.Store(true)
|
||||
fmt.Println("🔧 生产者设置 ready=true")
|
||||
|
||||
wg.Wait()
|
||||
fmt.Println("所有操作执行完成")
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
// bulma/atomic_memory_order.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var relaxedCounter atomic.Uint64 // 无符号原子计数器
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// 使用 Relaxed 顺序(仅保证原子性,无全局顺序约束)
|
||||
start := time.Now()
|
||||
for i := 0; i < 100; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 10000; j++ {
|
||||
// 第二个参数指定内存顺序:OrderRelaxed(无顺序约束,仅保证原子性)
|
||||
relaxedCounter.Add(1)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
fmt.Printf("✅ Relaxed 计数: %d (耗时: %v)\n",
|
||||
relaxedCounter.Load(),
|
||||
time.Since(start))
|
||||
// 注意:最终值正确,但中间状态可能"乱序"被其他 goroutine 观察到
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// bulma/atomic_mutex_hybrid.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// RequestCounter 请求计数器(混合方案:原子操作+Mutex)
|
||||
type RequestCounter struct {
|
||||
total atomic.Uint64 // 高频累加,用原子操作
|
||||
mu sync.Mutex // 互斥锁,保护复杂数据结构(map)
|
||||
details map[string]uint64 // 复杂结构,用 Mutex 保护
|
||||
}
|
||||
|
||||
// Inc 累加请求数:包含高频简单操作与复杂结构更新
|
||||
func (rc *RequestCounter) Inc(endpoint string) {
|
||||
rc.total.Add(1) // 无锁累加总数
|
||||
|
||||
// 复杂数据结构更新(map写入),通过加锁保护,避免数据竞争
|
||||
rc.mu.Lock()
|
||||
rc.details[endpoint]++
|
||||
rc.mu.Unlock()
|
||||
}
|
||||
|
||||
// Stats 获取统计结果:通过深拷贝,避免外部修改内部数据
|
||||
func (rc *RequestCounter) Stats() (uint64, map[string]uint64) {
|
||||
rc.mu.Lock()
|
||||
defer rc.mu.Unlock()
|
||||
// 深拷贝避免外部修改
|
||||
copyDetails := make(map[string]uint64, len(rc.details))
|
||||
for k, v := range rc.details {
|
||||
copyDetails[k] = v
|
||||
}
|
||||
return rc.total.Load(), copyDetails
|
||||
}
|
||||
|
||||
func main() {
|
||||
rc := &RequestCounter{details: make(map[string]uint64)}
|
||||
// 模拟3次接口请求
|
||||
rc.Inc("/api/users")
|
||||
rc.Inc("/api/posts")
|
||||
rc.Inc("/api/users")
|
||||
|
||||
// 查看统计结果
|
||||
total, details := rc.Stats()
|
||||
fmt.Printf("✅ 总请求数: %d\n", total)
|
||||
fmt.Printf("✅ 详情: %+v\n", details)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// bulma/atomic_pointer.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Value int
|
||||
Next *Node
|
||||
}
|
||||
|
||||
func main() {
|
||||
var head atomic.Pointer[Node]
|
||||
head.Store(&Node{Value: 10})
|
||||
|
||||
// 原子替换头节点
|
||||
old := head.Swap(&Node{Value: 20})
|
||||
fmt.Printf("✅ 旧头节点值: %d\n", old.Value)
|
||||
fmt.Printf("✅ 新头节点值: %d\n", head.Load().Value)
|
||||
|
||||
// CAS 安全更新(仅当当前值为预期时才更新)
|
||||
if head.CompareAndSwap(&Node{Value: 20}, &Node{Value: 30}) {
|
||||
fmt.Println("✅ CAS 成功:头节点更新为 30")
|
||||
} else {
|
||||
fmt.Println("❌ CAS 失败:头节点已被其他 goroutine 修改")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// bulma/atomic_spinlock.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SpinLock 自旋锁结构体(基于atomic.Bool实现)
|
||||
type SpinLock struct {
|
||||
flag atomic.Bool // false=未锁定,true=已锁定
|
||||
}
|
||||
|
||||
// Lock 加锁:自旋等待,直到获取锁
|
||||
func (l *SpinLock) Lock() {
|
||||
// CAS逻辑:尝试将flag从false改为true,失败则循环重试
|
||||
for l.flag.CompareAndSwap(false, true) {
|
||||
// 短暂休眠,避免忙等耗尽CPU(关键优化)
|
||||
time.Sleep(time.Nanosecond) // 避免忙等耗尽 CPU
|
||||
}
|
||||
}
|
||||
|
||||
// Unlock 解锁:将flag设为false,释放锁
|
||||
func (l *SpinLock) Unlock() {
|
||||
l.flag.Store(false)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var lock SpinLock
|
||||
var counter atomic.Int32 // 原子计数器,配合自旋锁使用
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// 10 个 goroutine 争抢自旋锁
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
lock.Lock() // 获取自旋锁
|
||||
defer lock.Unlock() // 延迟解锁,避免锁泄露
|
||||
|
||||
counter.Add(1)
|
||||
fmt.Printf("Goroutine %d 完成计数\n", id)
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
fmt.Printf("✅ 最终计数: %d (应为 10)\n", counter.Load())
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
// bulma/atomic_stack.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// StackNode 无锁栈节点(泛型,支持任意数据类型)
|
||||
type StackNode[T any] struct {
|
||||
Value T
|
||||
Next *StackNode[T] // 指向栈底节点
|
||||
}
|
||||
|
||||
// LockFreeStack 无锁栈(基于atomic.Pointer实现)
|
||||
type LockFreeStack[T any] struct {
|
||||
head atomic.Pointer[StackNode[T]] // 栈顶指针(原子指针)
|
||||
}
|
||||
|
||||
// Push 入栈:通过CAS操作替换栈顶,保证并发安全
|
||||
func (s *LockFreeStack[T]) Push(val T) {
|
||||
for {
|
||||
oldHead := s.head.Load()
|
||||
newHead := &StackNode[T]{Value: val, Next: oldHead}
|
||||
// CAS操作成功则入栈完成,失败则重试(避免并发冲突)
|
||||
if s.head.CompareAndSwap(oldHead, newHead) {
|
||||
return // CAS 成功,退出
|
||||
}
|
||||
// CAS 失败(被其他 goroutine 修改),重试
|
||||
}
|
||||
}
|
||||
|
||||
// Pop 出栈:通过CAS操作替换栈顶为下一个节点,无元素时返回false
|
||||
func (s *LockFreeStack[T]) Pop() (T, bool) {
|
||||
for {
|
||||
oldHead := s.head.Load()
|
||||
if oldHead == nil {
|
||||
var zero T // 空值,对应泛型默认零值
|
||||
return zero, false
|
||||
}
|
||||
// CAS操作成功则出栈完成,返回栈顶元素
|
||||
if s.head.CompareAndSwap(oldHead, oldHead.Next) {
|
||||
return oldHead.Value, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
stack := &LockFreeStack[int]{}
|
||||
// 入栈3个元素(10、20、30)
|
||||
stack.Push(10)
|
||||
stack.Push(20)
|
||||
stack.Push(30)
|
||||
// 出栈(LIFO:后进先出,依次弹出30、20、10)
|
||||
for i := 0; i < 3; i++ {
|
||||
if val, ok := stack.Pop(); ok {
|
||||
fmt.Printf("✅ 弹出: %d\n", val)
|
||||
}
|
||||
}
|
||||
// 空栈测试:弹出操作失败
|
||||
if _, ok := stack.Pop(); !ok {
|
||||
fmt.Println("栈已为空,弹出失败")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// bulma/atomic_stress_test_demo.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var counter atomic.Uint64
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// 压力测试配置:10000个goroutine,每个累加1000次,总计1亿次操作
|
||||
goroutineNum := 10000
|
||||
perGoroutineOps := 1000
|
||||
wg.Add(goroutineNum)
|
||||
|
||||
start := time.Now()
|
||||
for i := 0; i < goroutineNum; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < perGoroutineOps; j++ {
|
||||
counter.Add(1) // 默认使用OrderSeqCst,安全稳定
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
cost := time.Since(start)
|
||||
expected := uint64(goroutineNum * perGoroutineOps)
|
||||
actual := counter.Load()
|
||||
|
||||
// 验证结果:判断实际计数与预期计数是否一致,验证原子操作稳定性
|
||||
fmt.Printf("压力测试完成:%d个goroutine,每个执行%d次累加\n", goroutineNum, perGoroutineOps)
|
||||
fmt.Printf("预期计数: %d\n", expected)
|
||||
fmt.Printf("实际计数: %d\n", actual)
|
||||
fmt.Printf("执行耗时: %v\n", cost)
|
||||
fmt.Printf("操作吞吐量: %.2f万次/毫秒\n", float64(expected)/float64(cost.Milliseconds()))
|
||||
|
||||
if actual == expected {
|
||||
fmt.Println("原子操作稳定可靠,无数据竞争!")
|
||||
} else {
|
||||
fmt.Printf("原子操作异常,计数偏差: %d\n", expected-actual)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// bulma/atomic_value_trap.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var v atomic.Value
|
||||
v.Store("initial string") // 首次存储 string
|
||||
|
||||
// ❌ 错误:尝试存储不同类型的值(会 panic!)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
fmt.Println("⚠️ 捕获 panic:", r)
|
||||
fmt.Println("✅ 正确做法:始终存储相同类型(推荐指针)")
|
||||
}
|
||||
}()
|
||||
v.Store(123) // 运行时 panic: sync/atomic: store of inconsistently typed value into Value
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
var isSuccess bool
|
||||
fmt.Printf("布尔零值: %t\n", isSuccess) // 输出false
|
||||
|
||||
a, b := true, false
|
||||
|
||||
// 逻辑运算
|
||||
fmt.Printf("a && b: %t\n", a && b) // false(与)
|
||||
fmt.Printf("a || b: %t\n", a || b) // true(或)
|
||||
fmt.Printf("!a: %t\n", !a) // false(非)
|
||||
|
||||
// 条件判断中使用
|
||||
score := 85
|
||||
isPass := score >= 60
|
||||
if isPass {
|
||||
fmt.Println("考试通过") // 输出此句
|
||||
}
|
||||
|
||||
// 错误示例(编译不通过)
|
||||
// var b bool = 1 // 报错:cannot use 1 (untyped int constant) as bool value in variable declaration
|
||||
// fmt.Println(b + 1) // 报错:invalid operation: b + 1 (mismatched types bool and untyped int)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 整数类型
|
||||
var maxInt8 int8 = 127
|
||||
var minInt8 int8 = -128
|
||||
fmt.Printf("int8范围: %d ~ %d\n", minInt8, maxInt8)
|
||||
|
||||
// 字节与Unicode码点
|
||||
var b byte = 0x61 // 十六进制表示字符'a'
|
||||
var r rune = '中' // Unicode码点,值为20013
|
||||
fmt.Printf("byte('a'): %T=%d(字符:%c)\n", b, b, b) // uint8=97(字符:a)
|
||||
fmt.Printf("rune('中'): %T=%d(字符:%c)\n", r, r, r) // int32=20013(字符:中)
|
||||
|
||||
// uintptr与指针(底层内存操作,谨慎使用)
|
||||
var x int = 100
|
||||
p := &x // *int类型,存储x的地址
|
||||
ptrVal := uintptr(unsafe.Pointer(p)) // 转换为存储地址的数值
|
||||
fmt.Printf("x的地址:%p → uintptr:%x\n", p, ptrVal)
|
||||
|
||||
// 浮点数
|
||||
f32 := float32(3.1415926535)
|
||||
f64 := 3.141592653589793 // 默认float64
|
||||
fmt.Printf("float32: %.6f, float64: %.12f\n", f32, f64)
|
||||
|
||||
// 复数
|
||||
c := complex(3, 4) // 等价于3+4i(complex128)
|
||||
fmt.Printf("复数:%v,实部:%v,虚部:%v\n", c, real(c), imag(c)) // (3+4i) 3 4
|
||||
|
||||
// 复数运算
|
||||
var d complex128 = 1 - 2i
|
||||
fmt.Printf("c+d: %v, c*d: %v\n", c+d, c*d) // 输出4+2i, 11-2i
|
||||
|
||||
// 复数相等判断
|
||||
if real(c) == real(d) && imag(c) == imag(d) {
|
||||
fmt.Println("c与d相等")
|
||||
} else {
|
||||
fmt.Println("c与d不相等")
|
||||
}
|
||||
|
||||
// 浮点数比较(考虑精度)
|
||||
if math.Abs(f64-float64(f32)) < 1e-6 {
|
||||
fmt.Println("浮点数近似相等")
|
||||
} else {
|
||||
fmt.Println("浮点数不相等")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 字符串初始化
|
||||
s1 := "Golang语言"
|
||||
s2 := `多行字符串,
|
||||
小猫:喵喵喵,
|
||||
小狗:汪汪汪`
|
||||
|
||||
fmt.Println("s1:", s1)
|
||||
fmt.Println("s2:", s2)
|
||||
|
||||
// 字符串长度(字节数)
|
||||
fmt.Printf("s1长度(字节): %d\n", len(s1)) // 输出12("Golang"6字节 + "语言"6字节)
|
||||
|
||||
// 字符串遍历(for vs for range)
|
||||
fmt.Println("for遍历(按byte):")
|
||||
for i := 0; i < len(s1); i++ {
|
||||
fmt.Printf("%x ", s1[i]) // 输出字节的十六进制,中文会拆分为多字节
|
||||
}
|
||||
|
||||
fmt.Println("\nfor range遍历(按rune):")
|
||||
for _, r := range s1 {
|
||||
fmt.Printf("%c ", r) // 输出完整字符:G o l a n g 语 言
|
||||
}
|
||||
|
||||
// 字符串修改(需转[]rune)
|
||||
s3 := "hello"
|
||||
runes := []rune(s3)
|
||||
runes[0] = 'H'
|
||||
s3 = string(runes)
|
||||
fmt.Printf("\n修改后: %s\n", s3) // 输出Hello
|
||||
|
||||
// 高效字符串拼接
|
||||
var builder strings.Builder
|
||||
for i := 0; i < 5; i++ {
|
||||
builder.WriteString(fmt.Sprintf("part%d", i))
|
||||
}
|
||||
fmt.Println("拼接结果:", builder.String()) // part0part1part2part3part4
|
||||
}
|
||||
+1000
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 打开文件(创建+追加模式)
|
||||
file, err := os.OpenFile("batch_write.log",
|
||||
os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("文件打开失败:%v\n", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
// 初始化4KB缓冲区(默认4096字节,可按场景调整)
|
||||
writer := bufio.NewWriterSize(file, 4096)
|
||||
defer writer.Flush() // 退出前确保缓冲区数据写入文件
|
||||
|
||||
// 批量写入1000条记录
|
||||
for i := 0; i < 1000; i++ {
|
||||
fmt.Fprintf(writer, "批量记录%d:%s\n",
|
||||
i, "这是一条批量写入的数据")
|
||||
}
|
||||
|
||||
fmt.Println("批量写入完成,缓冲区数据将由defer自动Flush")
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const loopCount = 100000 // 10万次拼接
|
||||
var builder strings.Builder
|
||||
|
||||
// 预分配内存(预估每个item占10字节,避免动态扩容)
|
||||
builder.Grow(loopCount * 10)
|
||||
|
||||
start := time.Now()
|
||||
for i := 0; i < loopCount; i++ {
|
||||
fmt.Fprintf(&builder, "item_%d,", i) // 直接写入缓冲区,无临时字符串
|
||||
}
|
||||
result := builder.String()
|
||||
fmt.Printf("strings.Builder拼接耗时:%v\n", time.Since(start))
|
||||
_ = result // 避免未使用变量警告
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
const loopCount = 100000 // 10万次拼接
|
||||
var result string
|
||||
|
||||
start := time.Now()
|
||||
for i := 0; i < loopCount; i++ {
|
||||
result += fmt.Sprintf("item_%d,", i) // 每次拼接生成新字符串,内存分配频繁
|
||||
}
|
||||
fmt.Printf("fmt循环拼接耗时:%v\n", time.Since(start))
|
||||
_ = result // 避免未使用变量警告
|
||||
}
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// =============== ABA 问题演示(增强时序稳定性) ===============
|
||||
func demoABAProblem() {
|
||||
var value int32 = 100
|
||||
var wg sync.WaitGroup
|
||||
ready := make(chan struct{}) // 同步信号:确保 A 先读取
|
||||
|
||||
wg.Add(2)
|
||||
|
||||
// Goroutine A: 读取初始值后等待信号
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
old := atomic.LoadInt32(&value) // 读取到 100
|
||||
<-ready // 等待 B 完成 ABA 修改
|
||||
if atomic.CompareAndSwapInt32(&value, old, 200) {
|
||||
fmt.Println("⚠️ ABA 问题触发: CAS 成功!但值曾被篡改 (100→150→100)")
|
||||
} else {
|
||||
fmt.Println("❌ 未复现 ABA(调度异常)")
|
||||
}
|
||||
}()
|
||||
|
||||
// Goroutine B: 执行 A→B→A 修改
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
time.Sleep(5 * time.Millisecond) // 确保 A 已读取
|
||||
atomic.StoreInt32(&value, 150) // A→B
|
||||
atomic.StoreInt32(&value, 100) // B→A
|
||||
close(ready) // 通知 A 执行 CAS
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
fmt.Printf("最终值: %d (ABA 发生时应为 200)\n", value)
|
||||
}
|
||||
|
||||
// =============== 安全方案:位域编码防 ABA ===============
|
||||
// 64位结构: [高32位: 版本号] | [低32位: 实际值]
|
||||
type VersionedInt32 struct {
|
||||
data int64 // 原子存储单元
|
||||
}
|
||||
|
||||
// 合并值与版本号
|
||||
func pack(val, ver int32) int64 {
|
||||
return (int64(ver) << 32) | int64(uint32(val))
|
||||
}
|
||||
|
||||
// 拆解:返回 (值, 版本号)
|
||||
func unpack(data int64) (val int32, ver int32) {
|
||||
return int32(data), int32(data >> 32) // 低32位=值, 高32位=版本
|
||||
}
|
||||
|
||||
// 安全读取当前值
|
||||
func (v *VersionedInt32) Load() int32 {
|
||||
val, _ := unpack(atomic.LoadInt64(&v.data)) // 取第一个返回值
|
||||
return val
|
||||
}
|
||||
|
||||
// 带版本号的原子 CAS(彻底规避 ABA)
|
||||
func (v *VersionedInt32) CAS(oldVal, newVal int32) bool {
|
||||
for {
|
||||
current := atomic.LoadInt64(&v.data)
|
||||
curVal, curVer := unpack(current)
|
||||
|
||||
if curVal != oldVal {
|
||||
return false // 值已变更,拒绝更新
|
||||
}
|
||||
|
||||
// 构造新状态:值更新 + 版本号+1
|
||||
newState := pack(newVal, curVer+1)
|
||||
|
||||
if atomic.CompareAndSwapInt64(&v.data, current, newState) {
|
||||
return true
|
||||
}
|
||||
// 失败则重试
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("=== ABA 问题演示(通道同步确保时序) ===")
|
||||
demoABAProblem()
|
||||
|
||||
fmt.Println("\n=== 安全方案验证(位域编码) ===")
|
||||
vv := &VersionedInt32{data: pack(100, 0)} // 初始值=100, 版本=0
|
||||
|
||||
// 首次 CAS:100 → 200
|
||||
if vv.CAS(100, 200) {
|
||||
val := vv.Load()
|
||||
_, ver := unpack(atomic.LoadInt64(&vv.data))
|
||||
fmt.Printf("✅ CAS 成功 | 值: %d, 版本: %d\n", val, ver)
|
||||
}
|
||||
|
||||
// 尝试用过期旧值 100 更新(应失败)
|
||||
if !vv.CAS(100, 300) {
|
||||
fmt.Println("✅ 检测到非法修改:CAS 拒绝使用过期旧值更新")
|
||||
}
|
||||
|
||||
// 使用当前正确值更新
|
||||
if vv.CAS(200, 300) {
|
||||
fmt.Printf("✅ 安全更新成功 | 当前值: %d\n", vv.Load())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var counter int64
|
||||
var wg sync.WaitGroup
|
||||
const goroutines, increments = 10, 10000
|
||||
|
||||
wg.Add(goroutines)
|
||||
for i := 0; i < goroutines; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for j := 0; j < increments; j++ {
|
||||
for {
|
||||
old := atomic.LoadInt64(&counter)
|
||||
if atomic.CompareAndSwapInt64(&counter, old, old+1) {
|
||||
break // 成功则退出重试循环
|
||||
}
|
||||
// 失败:其他 goroutine 已修改,重试
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
fmt.Printf("✅ 最终计数值: %d (期望: %d)\n", counter, goroutines*increments)
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
type node struct {
|
||||
value interface{}
|
||||
next unsafe.Pointer
|
||||
}
|
||||
|
||||
type LockFreeStack struct {
|
||||
top unsafe.Pointer
|
||||
}
|
||||
|
||||
func (s *LockFreeStack) Push(v interface{}) {
|
||||
newNode := &node{value: v}
|
||||
for {
|
||||
oldTop := atomic.LoadPointer(&s.top)
|
||||
newNode.next = oldTop
|
||||
if atomic.CompareAndSwapPointer(&s.top, oldTop, unsafe.Pointer(newNode)) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *LockFreeStack) Pop() (interface{}, bool) {
|
||||
for {
|
||||
oldTop := atomic.LoadPointer(&s.top)
|
||||
if oldTop == nil {
|
||||
return nil, false // 栈空:返回 false
|
||||
}
|
||||
node := (*node)(oldTop)
|
||||
if atomic.CompareAndSwapPointer(&s.top, oldTop, node.next) {
|
||||
return node.value, true // 弹出成功:返回 true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
stack := &LockFreeStack{}
|
||||
var wg sync.WaitGroup
|
||||
const ops = 1000
|
||||
|
||||
wg.Add(2)
|
||||
// 生产者:压入 1000 个元素
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < ops; i++ {
|
||||
stack.Push(i)
|
||||
}
|
||||
}()
|
||||
|
||||
// 消费者:弹出 1000 个元素
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
count := 0
|
||||
for count < ops {
|
||||
if _, ok := stack.Pop(); ok {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
|
||||
// 🔑 核心修正:验证栈是否为空
|
||||
_, popped := stack.Pop() // popped = true 表示"成功弹出"(栈非空)
|
||||
if !popped { // !popped = true 表示"弹出失败"(栈已空)✅
|
||||
fmt.Println("✅ 无锁栈验证通过:所有元素正确入栈出栈,栈已空")
|
||||
} else {
|
||||
fmt.Println("❌ 栈非空,存在数据不一致(验证逻辑已修正)")
|
||||
}
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ===================== 一、Channel 类型系统 =====================
|
||||
// producer 只写通道 生产者
|
||||
func producer(ch chan<- int) {
|
||||
defer close(ch) // 延迟关闭,确保数据发送完毕
|
||||
for i := 1; i <= 2; i++ {
|
||||
ch <- i * 10
|
||||
}
|
||||
}
|
||||
|
||||
// consumer 只读通道 消费者
|
||||
func consumer(ch <-chan int) {
|
||||
for data := range ch {
|
||||
fmt.Printf("消费数据:%d\n", data)
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 二、Channel 缓冲策略 =====================
|
||||
func bufferDemo() {
|
||||
fmt.Println("\n===== 无缓冲通道(同步通信)=====")
|
||||
unbufCh := make(chan string)
|
||||
go func() {
|
||||
unbufCh <- "同步通道数据"
|
||||
}()
|
||||
fmt.Println(<-unbufCh)
|
||||
|
||||
fmt.Println("\n===== 有缓冲通道(异步通信)=====")
|
||||
bufCh := make(chan int, 3)
|
||||
bufCh <- 1
|
||||
bufCh <- 2
|
||||
// 监控缓冲状态
|
||||
fmt.Printf("已缓存数据:%d,总缓冲容量:%d\n", len(bufCh), cap(bufCh))
|
||||
close(bufCh)
|
||||
|
||||
for v := range bufCh {
|
||||
fmt.Println("异步数据:", v)
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 三、Channel 安全关闭 =====================
|
||||
func closeDemo() {
|
||||
fmt.Println("\n===== 通道安全关闭演示=====")
|
||||
ch := make(chan int, 2)
|
||||
// 保证通道仅关闭一次
|
||||
var closeOnce sync.Once
|
||||
|
||||
go func() {
|
||||
closeOnce.Do(func() {
|
||||
ch <- 100
|
||||
ch <- 200
|
||||
close(ch)
|
||||
fmt.Println("通道已安全关闭")
|
||||
})
|
||||
}()
|
||||
|
||||
// 安全读取数据
|
||||
for {
|
||||
val, ok := <-ch
|
||||
if !ok {
|
||||
fmt.Println("通道关闭,读取结束")
|
||||
break
|
||||
}
|
||||
fmt.Printf("读取值:%d\n", val)
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 四、Channel 高级用法 =====================
|
||||
func advancedDemo() {
|
||||
fmt.Println("\n===== select 多路复用=====")
|
||||
ch1, ch2 := make(chan string), make(chan string)
|
||||
|
||||
// 模拟异步任务
|
||||
go func() {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
ch1 <- "任务1执行完成"
|
||||
}()
|
||||
go func() {
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
ch2 <- "任务2执行完成"
|
||||
}()
|
||||
|
||||
// 监听多个通道
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case msg := <-ch1:
|
||||
fmt.Println(msg)
|
||||
case msg := <-ch2:
|
||||
fmt.Println(msg)
|
||||
}
|
||||
}
|
||||
|
||||
// 非阻塞读取
|
||||
fmt.Println("\n===== 非阻塞通道读取=====")
|
||||
ch := make(chan int, 1)
|
||||
select {
|
||||
case val := <-ch:
|
||||
fmt.Println("读取到数据:", val)
|
||||
default:
|
||||
fmt.Println("通道暂无数据,非阻塞跳过")
|
||||
}
|
||||
}
|
||||
|
||||
// ===================== 主函数 =====================
|
||||
func main() {
|
||||
fmt.Println("===== 一、Channel 类型系统演示=====")
|
||||
ch := make(chan int)
|
||||
go producer(ch)
|
||||
consumer(ch)
|
||||
|
||||
// 执行所有演示
|
||||
bufferDemo()
|
||||
closeDemo()
|
||||
advancedDemo()
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func printArray(a [3]int) {
|
||||
fmt.Println("函数内数组:", a)
|
||||
a[0] = 0 // 不会影响原数组
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 数组初始化
|
||||
var arr1 [3]int // 长度为3的int数组,零值初始化
|
||||
arr2 := [4]string{"a", "b", "c"} // 部分初始化,未指定的元素为零值
|
||||
arr3 := [...]int{10, 20, 30, 40} // 长度由初始化元素数量决定
|
||||
|
||||
fmt.Println("arr1:", arr1) // [0 0 0]
|
||||
fmt.Println("arr2:", arr2) // [a b c ]
|
||||
fmt.Println("arr3长度:", len(arr3)) // 4
|
||||
|
||||
// 数组访问与修改
|
||||
arr1[0] = 100
|
||||
arr1[1] = 200
|
||||
fmt.Println("arr1修改后:", arr1) // [100 200 0]
|
||||
|
||||
// 数组比较(元素类型可比较时)
|
||||
arr4 := [3]int{100, 200, 0}
|
||||
if arr1 == arr4 {
|
||||
fmt.Println("arr1与arr4相等")
|
||||
}
|
||||
|
||||
// 数组是值类型
|
||||
arr5 := arr1
|
||||
arr5[2] = 300
|
||||
fmt.Println("arr1:", arr1) // [100 200 0],未被修改
|
||||
fmt.Println("arr5:", arr5) // [100 200 300]
|
||||
|
||||
// 数组作为函数参数
|
||||
printArray(arr1)
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package main
|
||||
|
||||
/*import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// 工作函数,演示通道作为信号量
|
||||
func worker(id int, sem chan struct{}, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
// 获取信号量
|
||||
sem <- struct{}{}
|
||||
defer func() { <-sem }() // 释放信号量
|
||||
|
||||
fmt.Printf("Worker %d 开始工作\n", id)
|
||||
// 模拟工作
|
||||
for i := 0; i < 3; i++ {
|
||||
fmt.Printf("Worker %d 工作中...\n", id)
|
||||
}
|
||||
fmt.Printf("Worker %d 完成工作\n", id)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 带缓冲通道(容量2)
|
||||
chBuffered := make(chan int, 2)
|
||||
chBuffered <- 1 // 发送不阻塞(未达容量)
|
||||
chBuffered <- 2
|
||||
fmt.Println(<-chBuffered) // 接收:1
|
||||
fmt.Println(<-chBuffered) // 接收:2
|
||||
close(chBuffered)
|
||||
|
||||
// 关闭后发送会panic,接收返回零值
|
||||
if v, ok := <-chBuffered; ok {
|
||||
fmt.Println(v)
|
||||
} else {
|
||||
fmt.Println("通道已关闭") // 输出此句
|
||||
}
|
||||
|
||||
// 无缓冲通道(发送/接收需同时就绪)
|
||||
chUnbuffered := make(chan string)
|
||||
go func() {
|
||||
chUnbuffered <- "hello from goroutine" // 发送方:等待接收
|
||||
}()
|
||||
msg := <-chUnbuffered // 接收方:等待发送
|
||||
fmt.Println(msg) // 输出:hello from goroutine
|
||||
close(chUnbuffered)
|
||||
|
||||
// 通道实现信号量(限制并发数)
|
||||
var wg sync.WaitGroup
|
||||
sem := make(chan struct{}, 2) // 并发数限制为2
|
||||
|
||||
for i := 1; i <= 5; i++ {
|
||||
wg.Add(1)
|
||||
go worker(i, sem, &wg)
|
||||
}
|
||||
wg.Wait()
|
||||
close(sem)
|
||||
fmt.Println("所有工作完成")
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 映射创建
|
||||
m1 := make(map[string]int) // make创建
|
||||
m2 := map[string]int{ // 字面量初始化
|
||||
"apple": 5,
|
||||
"banana": 3,
|
||||
}
|
||||
|
||||
// 添加和修改元素
|
||||
m1["orange"] = 2
|
||||
m2["apple"] = 6 // 修改已有键的值
|
||||
|
||||
// 访问元素
|
||||
fmt.Println("m1[orange]:", m1["orange"]) // 2
|
||||
fmt.Println("m2[banana]:", m2["banana"]) // 3
|
||||
|
||||
// 判断键是否存在
|
||||
v, ok := m2["orange"]
|
||||
if ok {
|
||||
fmt.Printf("orange count: %d\n", v)
|
||||
} else {
|
||||
fmt.Println("orange not found") // 输出该结果
|
||||
}
|
||||
|
||||
// 删除元素
|
||||
delete(m2, "banana")
|
||||
fmt.Println("删除后m2[banana]:", m2["banana"]) // 0(零值)
|
||||
|
||||
// 映射长度
|
||||
fmt.Println("m2长度:", len(m2)) // 1
|
||||
|
||||
// 映射遍历(无序)
|
||||
fmt.Println("map遍历:")
|
||||
for fruit, count := range m2 {
|
||||
fmt.Printf("%s: %d\n", fruit, count) // 顺序不固定
|
||||
}
|
||||
|
||||
// 映射的键类型限制
|
||||
var m3 map[int]string // 整数
|
||||
var m4 map[string]bool // 字符串
|
||||
var m5 map[bool]int // 布尔值
|
||||
var m6 map[[3]int]string // 数组(元素类型也必须可比较)
|
||||
|
||||
// 结构体作为键(所有字段都必须可比较)
|
||||
type Point struct {
|
||||
X, Y int
|
||||
}
|
||||
var m7 map[Point]string
|
||||
fmt.Printf("m3:%T, m4:%T, m5:%T\n", m3, m4, m5)
|
||||
fmt.Printf("m6:%T, m7:%T\n", m6, m7)
|
||||
|
||||
// 特殊情况:NaN作为键(每个NaN都是唯一的)
|
||||
m := map[float64]string{}
|
||||
nan1 := math.NaN()
|
||||
nan2 := math.NaN()
|
||||
m[nan1] = "first"
|
||||
m[nan2] = "second"
|
||||
fmt.Println("NaN映射长度:", len(m)) // 2
|
||||
|
||||
// 并发安全映射(sync.Map)
|
||||
var m8 sync.Map
|
||||
m8.Store("cat", 2) // 存储键值对
|
||||
m8.Store("dog", 1)
|
||||
|
||||
// 读取与删除
|
||||
if v, ok := m8.Load("cat"); ok {
|
||||
fmt.Printf("cat count: %d\n", v) // 输出2
|
||||
}
|
||||
m8.Delete("dog")
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// 函数传指针,修改外部变量
|
||||
func increment(x *int) {
|
||||
*x++
|
||||
}
|
||||
|
||||
// 指针作为返回值
|
||||
func createInt() *int {
|
||||
var num int = 100
|
||||
return &num
|
||||
}
|
||||
|
||||
func main() {
|
||||
var x int = 20
|
||||
var p *int = &x // p是指向x的指针
|
||||
|
||||
fmt.Printf("x的值: %d, x的地址: %p\n", x, &x)
|
||||
fmt.Printf("p的值(地址): %p, p指向的值: %d\n", p, *p)
|
||||
|
||||
// 通过指针修改值
|
||||
*p = 21
|
||||
fmt.Printf("修改后x的值: %d\n", x) // 输出21
|
||||
|
||||
// 函数传指针
|
||||
increment(&x)
|
||||
fmt.Printf("increment后x值: %d\n", x) // 输出22
|
||||
|
||||
// 指针作为返回值
|
||||
p2 := createInt()
|
||||
fmt.Printf("createInt返回值: %d, 地址: %p\n", *p2, p2) // 输出100及地址
|
||||
|
||||
// 指针与nil比较
|
||||
var p3 *int
|
||||
if p3 == nil {
|
||||
fmt.Println("p3是nil指针")
|
||||
}
|
||||
|
||||
// 错误示例(编译不通过)
|
||||
// p++ // 报错:invalid operation: p++ (non-numeric type *int)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
// 切片创建
|
||||
s1 := []int{1, 2, 3} // 直接初始化
|
||||
s2 := make([]int, 3, 5) // make创建,长度3,容量5
|
||||
s3 := make([]int, 3) // 容量默认等于长度
|
||||
|
||||
fmt.Printf("s1: %v, len=%d, cap=%d\n", s1, len(s1), cap(s1)) // [1 2 3] 3 3
|
||||
fmt.Printf("s2: %v, len=%d, cap=%d\n", s2, len(s2), cap(s2)) // [0 0 0] 3 5
|
||||
fmt.Printf("s3: %v, len=%d, cap=%d\n", s3, len(s3), cap(s3)) // [0 0 0] 3 3
|
||||
|
||||
// 切片操作
|
||||
s4 := s1[1:3] // 切片表达式,从索引1到3(不包含3)
|
||||
fmt.Println("s4:", s4) // [2 3]
|
||||
|
||||
// 修改切片元素(影响底层数组)
|
||||
s4[0] = 20
|
||||
fmt.Println("修改后s1:", s1) // [1 20 3]
|
||||
fmt.Println("修改后s4:", s4) // [20 3]
|
||||
|
||||
// 切片追加元素
|
||||
s2 = append(s2, 400) // len=4 == cap=5 → 不扩容
|
||||
fmt.Printf("添加400后s2: %v, len=%d, cap=%d\n", s2, len(s2), cap(s2)) // [0 0 0 400] 4 5
|
||||
s2 = append(s2, 500) // len=5 == cap=5 → 不扩容
|
||||
fmt.Printf("添加500后s2: %v, len=%d, cap=%d\n", s2, len(s2), cap(s2)) // [0 0 0 400 500] 5 5
|
||||
s2 = append(s2, 600) // len=6 > cap=5 → 扩容(容量变为10)
|
||||
fmt.Printf("添加600后s2: %v, len=%d, cap=%d\n", s2, len(s2), cap(s2)) // [0 0 0 400 500 600] 6 10
|
||||
|
||||
// 复制切片
|
||||
dst := make([]int, 2)
|
||||
src := []int{10, 20, 30}
|
||||
copied := copy(dst, src) // 复制2个元素(取len(dst)和len(src)的最小值)
|
||||
fmt.Printf("复制后dst: %v, 复制数量: %d\n", dst, copied) // [10 20] 2
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 嵌套结构体(地址信息)
|
||||
type Address struct {
|
||||
City string
|
||||
Street string
|
||||
}
|
||||
|
||||
// 主结构体(用户信息)
|
||||
type User struct {
|
||||
ID int
|
||||
Name string
|
||||
Age int
|
||||
Address // 嵌套结构体(匿名字段,可直接访问其字段)
|
||||
}
|
||||
|
||||
// 带标签的结构体
|
||||
type Person struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age,omitempty"`
|
||||
}
|
||||
|
||||
// 结构体方法
|
||||
func (u *User) GetInfo() string {
|
||||
return fmt.Sprintf("User: %s, Age: %d, City: %s", u.Name, u.Age, u.City)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 初始化带嵌套的结构体
|
||||
u := User{
|
||||
ID: 1,
|
||||
Name: "Alice",
|
||||
Age: 20,
|
||||
Address: Address{
|
||||
City: "Beijing",
|
||||
Street: "Main St",
|
||||
},
|
||||
}
|
||||
|
||||
// 访问字段(嵌套字段可直接访问)
|
||||
fmt.Printf("用户:%+v\n", u)
|
||||
fmt.Println("城市:", u.City) // 等价于u.Address.City
|
||||
fmt.Println("用户信息:", u.GetInfo())
|
||||
|
||||
// 结构体是值类型:赋值时复制所有字段(包括嵌套字段)
|
||||
u2 := u
|
||||
u2.Age = 21
|
||||
u2.Street = "New St"
|
||||
fmt.Println("u年龄:", u.Age, "u2年龄:", u2.Age) // 20 21
|
||||
fmt.Println("u街道:", u.Street, "u2街道:", u2.Street) // Main St New St
|
||||
|
||||
// 结构体比较
|
||||
p1 := Person{Name: "Alice", Age: 25}
|
||||
p2 := Person{Name: "Alice", Age: 25}
|
||||
if p1 == p2 {
|
||||
fmt.Println("p1与p2相等")
|
||||
}
|
||||
|
||||
// 结构体标签:JSON序列化
|
||||
jsonP, _ := json.Marshal(p1)
|
||||
fmt.Printf("Person JSON: %s\n", jsonP) // 输出{"name":"Alice","age":25}
|
||||
|
||||
p3 := Person{Name: "Bob"} // Age为零值0
|
||||
jsonP3, _ := json.Marshal(p3)
|
||||
fmt.Printf("p3 JSON: %s\n", jsonP3) // 输出{"name":"Bob"}(Age为0被omitempty忽略)
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
[2025-10-31 18:24:38] goroutine9:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine9:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第0条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine5:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine4:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine2:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine1:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine7:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine8:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine3:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine6:完成第9条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第1条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第2条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第3条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第4条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第5条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第6条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第7条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第8条任务
|
||||
[2025-10-31 18:24:38] goroutine0:完成第9条任务
|
||||
@@ -0,0 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// SafeWriter 线程安全的Writer封装
|
||||
type SafeWriter struct {
|
||||
writer *os.File
|
||||
mu sync.Mutex // 互斥锁,保护Writer写入
|
||||
}
|
||||
|
||||
// NewSafeWriter 创建SafeWriter实例
|
||||
func NewSafeWriter(filename string) (*SafeWriter, error) {
|
||||
file, err := os.OpenFile(filename,
|
||||
os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SafeWriter{writer: file}, nil
|
||||
}
|
||||
|
||||
// Writef 线程安全的格式化写入
|
||||
func (w *SafeWriter) Writef(format string, args ...interface{}) error {
|
||||
w.mu.Lock() // 加锁,独占写入权
|
||||
defer w.mu.Unlock() // 函数退出自动解锁
|
||||
|
||||
// 拼接日志前缀(时间)+ 格式化内容
|
||||
logContent := fmt.Sprintf("[%s] %s\n",
|
||||
time.Now().Format("2006-01-02 15:04:05"),
|
||||
fmt.Sprintf(format, args...))
|
||||
|
||||
// 写入文件(用WriteString比Fprintf更高效)
|
||||
_, err := w.writer.WriteString(logContent)
|
||||
return err
|
||||
}
|
||||
|
||||
// Close 关闭文件
|
||||
func (w *SafeWriter) Close() error {
|
||||
return w.writer.Close()
|
||||
}
|
||||
|
||||
func main() {
|
||||
writer, err := NewSafeWriter("concurrent.log")
|
||||
if err != nil {
|
||||
fmt.Printf("SafeWriter创建失败:%v\n", err)
|
||||
return
|
||||
}
|
||||
defer writer.Close()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
// 10个goroutine并发写入(每个写10条)
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func(goroutineID int) {
|
||||
defer wg.Done()
|
||||
for j := 0; j < 10; j++ {
|
||||
writer.Writef("goroutine%d:完成第%d条任务", goroutineID, j)
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
fmt.Println("10个goroutine并发写入完成,内容无错乱")
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
queue []int // 共享队列(生产者-消费者模型)
|
||||
cmu sync.Mutex // 保护队列的互斥锁
|
||||
cond = sync.NewCond(&cmu) // 基于mu创建条件变量
|
||||
cwg sync.WaitGroup
|
||||
)
|
||||
|
||||
// 生产者:向队列添加数据
|
||||
func producer(id int) {
|
||||
defer cwg.Done()
|
||||
for i := 0; i < 3; i++ {
|
||||
cmu.Lock()
|
||||
data := id*10 + i
|
||||
queue = append(queue, data)
|
||||
fmt.Printf("生产者%d: 生产数据%d(队列长度%d)\n", id, data, len(queue))
|
||||
cmu.Unlock()
|
||||
|
||||
cond.Signal() // 唤醒一个等待的消费者(通知队列非空)
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// 消费者:等待队列非空后消费数据
|
||||
func consumer(id int) {
|
||||
defer cwg.Done()
|
||||
for i := 0; i < 2; i++ { // 限定消费次数,避免无限等待
|
||||
cmu.Lock()
|
||||
// 循环等待:避免虚假唤醒(被唤醒时条件可能仍不满足)
|
||||
for len(queue) == 0 {
|
||||
cond.Wait() // 释放mu并阻塞,被唤醒时重新获取mu
|
||||
}
|
||||
if len(queue) > 0 { // 确保队列不为空
|
||||
data := queue[0]
|
||||
queue = queue[1:]
|
||||
fmt.Printf("消费者%d: 消费数据%d(剩余长度%d)\n", id, data, len(queue))
|
||||
}
|
||||
cmu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
cwg.Add(2) // 2个生产者
|
||||
go producer(0)
|
||||
go producer(1)
|
||||
cwg.Add(3) // 3个消费者
|
||||
go consumer(0)
|
||||
go consumer(1)
|
||||
go consumer(2)
|
||||
cwg.Wait()
|
||||
fmt.Println("所有生产者和消费者完成")
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 自定义上下文键
|
||||
type key string
|
||||
|
||||
const RequestIDKey key = "request_id"
|
||||
|
||||
// 数据层:模拟数据库查询
|
||||
func dataLayer(ctx context.Context) {
|
||||
reqID, _ := ctx.Value(RequestIDKey).(string)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
fmt.Printf("[数据层] 请求[%s]:超时取消 | 错误:%v\n", reqID, ctx.Err())
|
||||
case <-time.After(1800 * time.Millisecond):
|
||||
fmt.Printf("[数据层] 请求[%s]:数据查询成功\n", reqID)
|
||||
}
|
||||
}
|
||||
|
||||
// 服务层:业务逻辑处理
|
||||
func serviceLayer(ctx context.Context) {
|
||||
reqID, _ := ctx.Value(RequestIDKey).(string)
|
||||
fmt.Printf("[服务层] 请求[%s]:开始业务处理\n", reqID)
|
||||
dataLayer(ctx)
|
||||
}
|
||||
|
||||
// API层:接口入口
|
||||
func apiLayer(ctx context.Context) {
|
||||
reqID, _ := ctx.Value(RequestIDKey).(string)
|
||||
fmt.Printf("[API层] 请求[%s]:接收客户端请求\n", reqID)
|
||||
serviceLayer(ctx)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 1. 上下文传递请求ID
|
||||
ctx := context.WithValue(context.Background(), RequestIDKey, "REQ-2026-COM-001")
|
||||
|
||||
// 2. 设置超时时间:1.5秒
|
||||
ctx, cancel := context.WithTimeout(ctx, 1500*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
// 3. 启动全链路调用
|
||||
apiLayer(ctx)
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
fmt.Println("\n主协程:请求生命周期完整结束")
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 监听上下文信号的任务函数
|
||||
func runTask(ctx context.Context, taskName string) {
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
fmt.Printf("任务[%s]:收到退出信号,终止执行\n", taskName)
|
||||
return
|
||||
default:
|
||||
fmt.Printf("任务[%s]:运行中...\n", taskName)
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 1. 创建父上下文
|
||||
parentCtx, cancelParent := context.WithCancel(context.Background())
|
||||
defer cancelParent()
|
||||
|
||||
// 2. 派生两个子上下文
|
||||
childCtx1, _ := context.WithCancel(parentCtx)
|
||||
childCtx2, _ := context.WithCancel(parentCtx)
|
||||
|
||||
// 3. 启动协程执行任务
|
||||
go runTask(childCtx1, "子任务1")
|
||||
go runTask(childCtx2, "子任务2")
|
||||
|
||||
// 4. 主协程运行2秒后取消父上下文
|
||||
fmt.Println("主协程:2秒后取消所有子任务")
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
fmt.Println("\n主协程:取消父上下文")
|
||||
cancelParent()
|
||||
|
||||
// 等待协程优雅退出
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("主协程:所有任务已退出")
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 模拟耗时业务任务
|
||||
func businessTask(ctx context.Context, name string, duration time.Duration) {
|
||||
fmt.Printf("任务[%s]:启动,预计耗时%v\n", name, duration)
|
||||
|
||||
select {
|
||||
// 任务正常执行完成
|
||||
case <-time.After(duration):
|
||||
fmt.Printf("任务[%s]:执行完成\n", name)
|
||||
// 上下文超时/取消触发
|
||||
case <-ctx.Done():
|
||||
fmt.Printf("任务[%s]:被终止,原因:%v\n", name, ctx.Err())
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 创建全局超时上下文:2秒
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// 任务1:1秒完成(正常)
|
||||
go businessTask(ctx, "正常任务", 1*time.Second)
|
||||
// 任务2:3秒完成(超时)
|
||||
go businessTask(ctx, "超时任务", 3*time.Second)
|
||||
|
||||
// 等待所有任务执行完毕
|
||||
time.Sleep(3 * time.Second)
|
||||
fmt.Println("主协程:任务流程全部结束")
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 自定义键类型,防止键名冲突
|
||||
type ctxKey string
|
||||
|
||||
// 定义上下文键常量
|
||||
const (
|
||||
TraceID ctxKey = "trace_id"
|
||||
UserID ctxKey = "user_id"
|
||||
)
|
||||
|
||||
// 业务处理函数:从上下文获取元数据
|
||||
func handleRequest(ctx context.Context) {
|
||||
// 安全类型断言获取值
|
||||
traceID, ok := ctx.Value(TraceID).(string)
|
||||
if !ok {
|
||||
fmt.Println("未获取到追踪ID")
|
||||
return
|
||||
}
|
||||
|
||||
userID, ok := ctx.Value(UserID).(int)
|
||||
if !ok {
|
||||
fmt.Println("未获取到用户 ID")
|
||||
return
|
||||
}
|
||||
fmt.Printf("请求处理中:TraceID=%s,UserID=%d\n", traceID, userID)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 初始化根上下文
|
||||
ctx := context.Background()
|
||||
|
||||
// 存入请求链路元数据
|
||||
ctx = context.WithValue(ctx, TraceID, "20260324-CTX-001")
|
||||
ctx = context.WithValue(ctx, UserID, 10000) // int
|
||||
|
||||
// 传递上下文到业务函数
|
||||
handleRequest(ctx)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// AppError 全局通用自定义错误
|
||||
type AppError struct {
|
||||
Code int // 业务错误码
|
||||
Msg string // 错误描述
|
||||
Module string // 所属模块
|
||||
Status int // HTTP 状态码(可选扩展)
|
||||
Err error // 底层原始错误(支持错误链)
|
||||
}
|
||||
|
||||
// Error 实现标准 error 接口
|
||||
func (e *AppError) Error() string {
|
||||
return fmt.Sprintf("[%s] 错误码:%d | HTTP:%d | 描述:%s", e.Module, e.Code, e.Status, e.Msg)
|
||||
}
|
||||
|
||||
// Unwrap 返回底层错误,支持 errors.Unwrap 和 errors.As 递归解包
|
||||
func (e *AppError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// 模拟数据库操作错误
|
||||
func dbOperation(id string) error {
|
||||
if id == "not_found" {
|
||||
return fmt.Errorf("record [%s] not found: %w", id, errors.New("database empty"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 模拟业务逻辑
|
||||
func businessDemo(name string) error {
|
||||
if name == "invalid" {
|
||||
return &AppError{
|
||||
Code: 40010,
|
||||
Msg: "参数非法",
|
||||
Module: "user",
|
||||
Status: 400,
|
||||
}
|
||||
}
|
||||
|
||||
if name == "db_error" {
|
||||
dbErr := dbOperation("not_found")
|
||||
return &AppError{
|
||||
Code: 50001,
|
||||
Msg: "数据库操作失败",
|
||||
Module: "database",
|
||||
Status: 500,
|
||||
Err: dbErr, // 包装底层错误
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 统一错误处理
|
||||
func parseError(err error) {
|
||||
var appErr *AppError
|
||||
// 断言自定义错误
|
||||
if errors.As(err, &appErr) {
|
||||
fmt.Printf("捕获业务错误:%+v\n", appErr)
|
||||
|
||||
// 如果有底层错误,继续解包
|
||||
if appErr.Unwrap() != nil {
|
||||
fmt.Printf("底层错误:%v\n", appErr.Unwrap())
|
||||
|
||||
// 使用 errors.Is 检测底层错误
|
||||
if errors.Is(appErr.Unwrap(), errors.New("database empty")) {
|
||||
fmt.Println("判定:底层是数据库空错误")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
// 处理通用错误
|
||||
if err != nil {
|
||||
fmt.Printf("捕获系统错误:%v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println("===== 自定义错误类型 =====")
|
||||
err := businessDemo("invalid")
|
||||
parseError(err)
|
||||
|
||||
fmt.Println("\n===== 带错误链的自定义错误 =====")
|
||||
err2 := businessDemo("db_error")
|
||||
parseError(err2)
|
||||
|
||||
fmt.Println("\n===== 使用 errors.As 深度提取 =====")
|
||||
var appErr *AppError
|
||||
if errors.As(err2, &appErr) {
|
||||
fmt.Printf("提取到 AppError - 模块:%s, 错误码:%d\n", appErr.Module, appErr.Code)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1122
|
||||
@@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//go:embed version.txt
|
||||
var version string
|
||||
|
||||
func main() {
|
||||
fmt.Println("Version:", version)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
1.0.0
|
||||
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
// 定义格式字符串常量(避免硬编码导致的数量/格式错误)
|
||||
const userFormat = "用户信息:ID=%d,姓名=%s,年龄=%d \n"
|
||||
|
||||
// 1. 处理参数类型不匹配错误
|
||||
_, err := fmt.Printf(userFormat, "invalid_id", "sanmiao", 20) // ID应为int,却传string
|
||||
if err != nil {
|
||||
fmt.Printf("格式化错误(类型不匹配):%v\n", err)
|
||||
}
|
||||
// 输出:用户信息:ID=%!d(string=invalid_id),姓名=sanmiao,年龄=20
|
||||
|
||||
// 2. 处理参数数量不匹配错误
|
||||
_, err = fmt.Printf(userFormat, 1001, "sanmiao") // 缺少“年龄”参数
|
||||
if err != nil {
|
||||
fmt.Printf("格式化错误(数量不匹配):%v\n", err)
|
||||
}
|
||||
// 输出:用户信息:ID=1001,姓名=sanmiao,年龄=%!d(MISSING)
|
||||
|
||||
// 3. 正确用法
|
||||
_, err = fmt.Printf(userFormat, 1001, "sanmiao", 20)
|
||||
if err == nil {
|
||||
fmt.Println("\n格式化成功!")
|
||||
}
|
||||
// 输出:用户信息:ID=1001,姓名=sanmiao,年龄=20
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package main
|
||||
|
||||
//
|
||||
//import (
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "os"
|
||||
//)
|
||||
//
|
||||
//// 自定义全局错误
|
||||
//type BizError struct {
|
||||
// Code int
|
||||
// Msg string
|
||||
// Module string
|
||||
// Err error // 添加底层错误
|
||||
//}
|
||||
//
|
||||
//func (e *BizError) Error() string {
|
||||
// return fmt.Sprintf("[%s] %d: %s", e.Module, e.Code, e.Msg)
|
||||
//}
|
||||
//
|
||||
//// Unwrap 返回底层错误,支持 errors.Is 和 errors.As 递归解包
|
||||
//func (e *BizError) Unwrap() error {
|
||||
// return e.Err
|
||||
//}
|
||||
//
|
||||
//// 哨兵错误
|
||||
//var ErrFileNotFound = errors.New("file not found")
|
||||
//
|
||||
//// 底层方法:包装系统错误
|
||||
//func open(name string) error {
|
||||
// _, err := os.Open(name)
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("open exec: %w", ErrFileNotFound)
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//// 业务方法:包装底层错误 + 自定义错误
|
||||
//func handleFile(name string) error {
|
||||
// if err := open(name); err != nil {
|
||||
// // 触发自定义业务错误
|
||||
// return &BizError{
|
||||
// Code: 50001,
|
||||
// Msg: "文件处理异常",
|
||||
// Module: "file",
|
||||
// Err: err, // 包装底层错误
|
||||
// }
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//// 统一错误解析
|
||||
//func resolveError(err error) {
|
||||
// if err == nil {
|
||||
// return
|
||||
// }
|
||||
// fmt.Printf("完整错误:%v\n", err)
|
||||
//
|
||||
// // 1. 断言自定义错误
|
||||
// var biz *BizError
|
||||
// if errors.As(err, &biz) {
|
||||
// fmt.Printf("自定义错误:%v\n", biz)
|
||||
// // 2. 如果有底层错误,递归检查哨兵错误
|
||||
// if biz.Unwrap() != nil {
|
||||
// fmt.Printf("底层错误:%v\n", biz.Unwrap())
|
||||
//
|
||||
// // 3. 检查哨兵错误
|
||||
// if errors.Is(biz.Unwrap(), ErrFileNotFound) {
|
||||
// fmt.Println("✓ 哨兵错误:文件不存在")
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// // 4. 直接检查哨兵错误(兼容简单错误链)
|
||||
// if errors.Is(err, ErrFileNotFound) {
|
||||
// fmt.Println("✓ 哨兵错误:文件不存在")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 5. 服务降级
|
||||
// fmt.Println("执行降级:返回默认文件内容")
|
||||
//}
|
||||
//
|
||||
//func main() {
|
||||
// fmt.Println("===== Go 错误处理综合实战 =====")
|
||||
// err := handleFile("demo.txt")
|
||||
// resolveError(err)
|
||||
// fmt.Println("\n===== 测试正常情况 =====")
|
||||
// // 创建一个存在的文件进行测试
|
||||
// tmpFile := "temp_test.txt"
|
||||
// os.WriteFile(tmpFile, []byte("test"), 0644)
|
||||
// err2 := handleFile(tmpFile)
|
||||
// if err2 == nil {
|
||||
// fmt.Println("文件处理成功")
|
||||
// }
|
||||
// os.Remove(tmpFile)
|
||||
//}
|
||||
@@ -0,0 +1,154 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// ------------------------------
|
||||
// 错误类型定义(核心优化)
|
||||
// ------------------------------
|
||||
|
||||
// FileError 自定义文件服务错误结构体
|
||||
type FileError struct {
|
||||
Code int // 错误码
|
||||
Module string // 所属模块
|
||||
Message string // 错误信息
|
||||
Err error // 原始错误(支持错误包装/解包)
|
||||
}
|
||||
|
||||
// Error 实现 error 接口
|
||||
func (e *FileError) Error() string {
|
||||
if e.Err != nil {
|
||||
return fmt.Sprintf("[%s:%d] %s: %v", e.Module, e.Code, e.Message, e.Err)
|
||||
}
|
||||
return fmt.Sprintf("[%s:%d] %s", e.Module, e.Code, e.Message)
|
||||
}
|
||||
|
||||
// Unwrap 实现 errors.Unwrap 支持错误链解包
|
||||
func (e *FileError) Unwrap() error {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
// 预定义错误(使用 sentinel error 规范)
|
||||
var ErrFileNotFound = errors.New("file not found")
|
||||
|
||||
// ------------------------------
|
||||
// 业务服务实现
|
||||
// ------------------------------
|
||||
|
||||
// FileService 基础文件服务
|
||||
func FileService(ctx context.Context, filename string) error {
|
||||
// 模块名称常量
|
||||
const module = "FileService"
|
||||
|
||||
// 1. 空文件名校验(400)
|
||||
if filename == "" {
|
||||
return &FileError{
|
||||
Code: 400,
|
||||
Module: module,
|
||||
Message: "filename cannot be empty",
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 文件不存在模拟(404)
|
||||
return &FileError{
|
||||
Code: 404,
|
||||
Module: module,
|
||||
Message: "file does not exist",
|
||||
Err: ErrFileNotFound, // 包装原始哨兵错误
|
||||
}
|
||||
}
|
||||
|
||||
// AdvancedFileService 高级文件服务(包装基础服务)
|
||||
func AdvancedFileService(ctx context.Context, filename string) error {
|
||||
// 调用底层服务并包装错误信息
|
||||
if err := FileService(ctx, filename); err != nil {
|
||||
return fmt.Errorf("advanced file service failed: %s %w", filename, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ------------------------------go vet
|
||||
// 测试用例
|
||||
// ------------------------------
|
||||
|
||||
func TestFileService(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
// 子测试用例(清晰分组,便于定位失败)
|
||||
t.Run("file not found returns 404 error", func(t *testing.T) {
|
||||
err := FileService(ctx, "test.txt")
|
||||
|
||||
// 断言哨兵错误
|
||||
if !errors.Is(err, ErrFileNotFound) {
|
||||
t.Fatalf("期望错误: %v, 实际错误: %v", ErrFileNotFound, err)
|
||||
}
|
||||
|
||||
// 断言自定义错误类型与字段
|
||||
var fErr *FileError
|
||||
if !errors.As(err, &fErr) {
|
||||
t.Fatal("期望返回 *FileError 类型")
|
||||
}
|
||||
if fErr.Code != 404 {
|
||||
t.Errorf("错误码期望 404, 实际 %d", fErr.Code)
|
||||
}
|
||||
if fErr.Module != "FileService" {
|
||||
t.Errorf("模块期望 FileService, 实际 %s", fErr.Module)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("empty filename returns 400 error", func(t *testing.T) {
|
||||
err := FileService(ctx, "")
|
||||
if err == nil {
|
||||
t.Fatal("空文件名必须返回错误")
|
||||
}
|
||||
|
||||
var fErr *FileError
|
||||
if !errors.As(err, &fErr) {
|
||||
t.Fatal("期望返回 *FileError 类型")
|
||||
}
|
||||
if fErr.Code != 400 {
|
||||
t.Errorf("错误码期望 400, 实际 %d", fErr.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestErrorWrapping(t *testing.T) {
|
||||
original := errors.New("original error")
|
||||
wrapped := fmt.Errorf("wrapped: %w", original)
|
||||
|
||||
// 测试 errors.Is 错误链匹配
|
||||
if !errors.Is(wrapped, original) {
|
||||
t.Error("errors.Is 应当匹配包装后的错误")
|
||||
}
|
||||
|
||||
// 测试 errors.Unwrap 解包
|
||||
unwrapped := errors.Unwrap(wrapped)
|
||||
if unwrapped != original {
|
||||
t.Errorf("解包后应当返回原始错误, 实际: %v", unwrapped)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdvancedFileService(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
err := AdvancedFileService(ctx, "nonexistent.txt")
|
||||
if err == nil {
|
||||
t.Fatal("必须返回错误")
|
||||
}
|
||||
|
||||
// 错误信息包含文件名
|
||||
if !strings.Contains(err.Error(), "nonexistent.txt") {
|
||||
t.Errorf("错误信息应当包含文件名, 实际: %v", err)
|
||||
}
|
||||
|
||||
// 错误链中能找到自定义 FileError
|
||||
var fErr *FileError
|
||||
if !errors.As(err, &fErr) {
|
||||
t.Error("错误链中应当包含 *FileError")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 哨兵错误
|
||||
var (
|
||||
ErrCacheOffline = errors.New("cache offline")
|
||||
ErrRequestTimeout = errors.New("request timeout")
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("===== 错误处理模式 =====")
|
||||
|
||||
// 1. 快速失败
|
||||
if err := errors.New("config load failed"); err != nil {
|
||||
// 测试后面的把这一行 log.Fatalln 注释掉
|
||||
// log.Fatalln("快速失败:服务启动终止 ->", err)
|
||||
}
|
||||
|
||||
// 2. 服务降级
|
||||
cacheErr := fmt.Errorf("get data failed: %w", ErrCacheOffline)
|
||||
if errors.Is(cacheErr, ErrCacheOffline) {
|
||||
fmt.Println("降级策略:缓存故障,使用数据库默认数据")
|
||||
}
|
||||
|
||||
// 3. 容错忽略
|
||||
fmt.Println("容错处理:埋点上报失败,忽略不影响主流程")
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package main
|
||||
|
||||
//
|
||||
//import (
|
||||
// "errors"
|
||||
// "fmt"
|
||||
// "os"
|
||||
//)
|
||||
//
|
||||
//// 自定义错误类型:文件不存在错误
|
||||
//type FileNotExistError struct {
|
||||
// Filename string
|
||||
//}
|
||||
//
|
||||
//func (e *FileNotExistError) Error() string {
|
||||
// return fmt.Sprintf("file [%s] does not exist", e.Filename)
|
||||
//}
|
||||
//
|
||||
//// 多层函数调用:模拟文件操作 + 错误包装
|
||||
//func readFile(name string) error {
|
||||
// if err := openFile(name); err != nil {
|
||||
// // 上层包装:添加上下文
|
||||
// return fmt.Errorf("readFile failed: %w", err)
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func openFile(name string) error {
|
||||
// // 底层错误:系统原生错误
|
||||
// _, err := os.Open(name)
|
||||
// if err != nil {
|
||||
// // 底层包装:绑定文件名上下文
|
||||
// return fmt.Errorf("open file [%s] failed: %w", name, err)
|
||||
// }
|
||||
// return nil
|
||||
//}
|
||||
//
|
||||
//func main() {
|
||||
// fmt.Println("===== 错误包装与链追踪 =====")
|
||||
// err := readFile("not_exist.txt")
|
||||
//
|
||||
// // 输出完整错误链
|
||||
// fmt.Println("完整错误:", err)
|
||||
// // 单次解包
|
||||
// fmt.Println("解包一层:", errors.Unwrap(err))
|
||||
// // 递归检测系统错误
|
||||
// if errors.Is(err, os.ErrNotExist) {
|
||||
// fmt.Println("判定结果:文件不存在")
|
||||
// }
|
||||
//
|
||||
// fmt.Println("\n===== 使用 errors.As 提取特定错误类型 =====")
|
||||
//
|
||||
// // 演示 errors.As:提取路径错误类型
|
||||
// var pathErr *os.PathError
|
||||
// if errors.As(err, &pathErr) {
|
||||
// fmt.Printf("提取到 PathError: 操作=%s, 路径=%s\n", pathErr.Op, pathErr.Path)
|
||||
// }
|
||||
//
|
||||
// // 演示 errors.As:提取系统错误
|
||||
// var sysErr error
|
||||
// if errors.As(err, &sysErr) {
|
||||
// if errors.Is(sysErr, os.ErrNotExist) {
|
||||
// fmt.Println("通过 errors.As+Is 确认:底层是 ErrNotExist")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 自定义错误类型示例
|
||||
// fmt.Println("\n===== 自定义错误类型示例 =====")
|
||||
// customErr := &FileNotExistError{Filename: "test.txt"}
|
||||
// wrappedCustom := fmt.Errorf("config load failed: %w", customErr)
|
||||
//
|
||||
// var fileErr *FileNotExistError
|
||||
// if errors.As(wrappedCustom, &fileErr) {
|
||||
// fmt.Printf("提取到自定义错误类型:文件名=%s\n", fileErr.Filename)
|
||||
// }
|
||||
//}
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Person4 struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// closureEscape 返回闭包,捕获的变量逃逸
|
||||
func closureEscape() func() *Person4 {
|
||||
p := &Person4{Name: "Closure Person", Age: 30} // p 逃逸至堆
|
||||
return func() *Person4 {
|
||||
return p // 闭包引用p,延长其生命周期
|
||||
}
|
||||
}
|
||||
|
||||
// goroutineClosure 协程中使用闭包,捕获变量逃逸
|
||||
func goroutineClosure() {
|
||||
x := 100 // x 逃逸至堆
|
||||
go func() {
|
||||
fmt.Printf("协程闭包捕获:%d\n", x)
|
||||
}()
|
||||
}
|
||||
|
||||
func main() {
|
||||
closureFunc := closureEscape()
|
||||
p := closureFunc()
|
||||
fmt.Printf("闭包捕获变量:%+v\n", *p)
|
||||
|
||||
goroutineClosure()
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Animal 接口定义
|
||||
type Animal interface {
|
||||
Speak() string
|
||||
}
|
||||
|
||||
// Dog 实现Animal接口
|
||||
type Dog struct{}
|
||||
|
||||
func (d Dog) Speak() string {
|
||||
return "汪汪汪"
|
||||
}
|
||||
|
||||
// Cat 实现Animal接口
|
||||
type Cat struct{}
|
||||
|
||||
func (c Cat) Speak() string {
|
||||
return "喵喵喵"
|
||||
}
|
||||
|
||||
// interfaceEscape 变量赋值给接口,触发逃逸
|
||||
func interfaceEscape(animalType string) Animal {
|
||||
var a Animal
|
||||
if animalType == "dog" {
|
||||
d := Dog{} // d 逃逸至堆
|
||||
a = d
|
||||
} else {
|
||||
c := Cat{} // c 逃逸至堆
|
||||
a = c
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// interfacePointer 指针赋值给接口,触发逃逸
|
||||
func interfacePointer() interface{} {
|
||||
p := &Dog{} // p 逃逸至堆
|
||||
return p
|
||||
}
|
||||
|
||||
func main() {
|
||||
animal := interfaceEscape("dog")
|
||||
fmt.Printf("接口动态派发:%s\n", animal.Speak())
|
||||
|
||||
iface := interfacePointer()
|
||||
if dog, ok := iface.(*Dog); ok {
|
||||
fmt.Printf("接口指针逃逸:%s\n", dog.Speak())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// largeArrayEscape 大数组指针返回,触发逃逸
|
||||
func largeArrayEscape() *[10000]int {
|
||||
arr := new([10000]int) // 8MB(int占8字节),超过栈阈值,逃逸至堆
|
||||
for i := 0; i < 10000; i++ {
|
||||
arr[i] = i
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
// smallArrayNoEscape 小数组,不逃逸
|
||||
func smallArrayNoEscape() [10]int {
|
||||
var arr [10]int
|
||||
for i := 0; i < 10; i++ {
|
||||
arr[i] = i
|
||||
}
|
||||
return arr // 值拷贝,不逃逸
|
||||
}
|
||||
|
||||
func main() {
|
||||
largeArr := largeArrayEscape()
|
||||
fmt.Printf("大数组逃逸:长度=%d\n", len(largeArr))
|
||||
|
||||
smallArr := smallArrayNoEscape()
|
||||
fmt.Printf("小数组不逃逸:%v\n", smallArr)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Person8 struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// noEscape 返回值类型,不逃逸
|
||||
func noEscape() Person8 {
|
||||
p := Person8{Name: "No Escape", Age: 25}
|
||||
return p // 值拷贝,生命周期局限于函数内
|
||||
}
|
||||
|
||||
// localVar 局部变量,未被外部引用,不逃逸
|
||||
func localVar() {
|
||||
x := 100
|
||||
y := "hello"
|
||||
p := Person8{Name: "Local", Age: 30}
|
||||
fmt.Printf("局部变量不逃逸:x=%d, y=%s, p=%+v\n", x, y, p)
|
||||
}
|
||||
|
||||
func main() {
|
||||
p := noEscape()
|
||||
fmt.Printf("返回值不逃逸:%+v\n", p)
|
||||
|
||||
localVar()
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// 优化1:避免不必要的指针传递(小结构体)
|
||||
// 优化前:指针传递,可能触发逃逸
|
||||
func processPersonOld(p *Person) string {
|
||||
return fmt.Sprintf("%s is %d years old", p.Name, p.Age)
|
||||
}
|
||||
|
||||
// 优化后:值传递,不逃逸
|
||||
func processPersonNew(p Person) string {
|
||||
return fmt.Sprintf("%s is %d years old", p.Name, p.Age)
|
||||
}
|
||||
|
||||
// 优化2:提前确定切片容量,避免动态扩容逃逸
|
||||
// 优化前:动态扩容,底层数组逃逸
|
||||
func sliceEscape() []int {
|
||||
s := make([]int, 0)
|
||||
for i := 0; i < 100; i++ {
|
||||
s = append(s, i)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 优化后:指定容量,不逃逸
|
||||
func sliceNoEscape() []int {
|
||||
s := make([]int, 0, 100) // 编译期确定容量
|
||||
for i := 0; i < 100; i++ {
|
||||
s = append(s, i)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// 优化3:使用对象池复用对象,减少堆分配
|
||||
var personPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &Person{} // 初始化对象,仅分配一次(或按需分配)
|
||||
},
|
||||
}
|
||||
|
||||
// 优化前:频繁创建对象,多次堆分配
|
||||
func createPersonOld(name string, age int) *Person {
|
||||
return &Person{Name: name, Age: age} // 每次调用都逃逸,触发堆分配
|
||||
}
|
||||
|
||||
// 优化后:复用对象池中的对象,减少堆分配
|
||||
func createPersonNew(name string, age int) *Person {
|
||||
p := personPool.Get().(*Person)
|
||||
p.Name = name
|
||||
p.Age = age
|
||||
return p
|
||||
}
|
||||
|
||||
// 释放对象到池
|
||||
func releasePerson(p *Person) {
|
||||
// 重置对象状态(避免残留数据)
|
||||
p.Name = ""
|
||||
p.Age = 0
|
||||
personPool.Put(p)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 优化1测试
|
||||
p := Person{Name: "Alice", Age: 25}
|
||||
fmt.Println("优化1-值传递:", processPersonNew(p))
|
||||
|
||||
// 优化2测试
|
||||
s1 := sliceEscape()
|
||||
s2 := sliceNoEscape()
|
||||
fmt.Printf("优化2-切片:逃逸len=%d, 不逃逸len=%d\n", len(s1), len(s2))
|
||||
|
||||
// 优化3测试
|
||||
p1 := createPersonNew("Bob", 30)
|
||||
fmt.Println("优化3-对象池:", *p1)
|
||||
releasePerson(p1)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Person7 struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
type Manager struct {
|
||||
Person7 *Person7 // 关联Person指针
|
||||
}
|
||||
|
||||
// GetPerson 返回关联的Person指针,触发逃逸
|
||||
func (m *Manager) GetPerson() *Person7 {
|
||||
return m.Person7 // Person指针逃逸
|
||||
}
|
||||
|
||||
// createManager 返回Manager指针,触发逃逸
|
||||
func createManager() *Manager {
|
||||
m := &Manager{
|
||||
Person7: &Person7{Name: "Manager Person", Age: 40}, // Person逃逸
|
||||
}
|
||||
return m // Manager指针逃逸
|
||||
}
|
||||
|
||||
func main() {
|
||||
manager := createManager()
|
||||
person := manager.GetPerson()
|
||||
fmt.Printf("方法接收者逃逸:%+v\n", *person)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// 全局变量,存储Person指针
|
||||
var globalPerson *Person2
|
||||
|
||||
// 全局切片,存储Person指针
|
||||
var globalPersonSlice []*Person2
|
||||
|
||||
type Person2 struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// storePerson 接收指针参数并返回,触发逃逸
|
||||
func storePerson(p *Person2) *Person2 {
|
||||
return p // 指针被返回,触发逃逸
|
||||
}
|
||||
|
||||
// addToGlobal 将指针存入全局变量,触发逃逸
|
||||
func addToGlobal() {
|
||||
p := &Person2{Name: "Bob", Age: 30}
|
||||
globalPerson = p // 存入全局变量,p逃逸
|
||||
}
|
||||
|
||||
// addToGlobalSlice 将指针存入全局切片,触发逃逸
|
||||
func addToGlobalSlice() {
|
||||
for i := 0; i < 3; i++ {
|
||||
p := &Person2{Name: fmt.Sprintf("Person%d", i), Age: 20 + i}
|
||||
globalPersonSlice = append(globalPersonSlice, p) // p逃逸至切片
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
p := &Person2{Name: "Charlie", Age: 28}
|
||||
storedP := storePerson(p)
|
||||
fmt.Printf("引用传递:%+v\n", *storedP)
|
||||
|
||||
addToGlobal()
|
||||
fmt.Printf("全局变量存储:%+v\n", *globalPerson)
|
||||
|
||||
addToGlobalSlice()
|
||||
fmt.Printf("全局切片存储:%d个Person\n", len(globalPersonSlice))
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Person 测试结构体
|
||||
type Person struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// newPerson 返回局部变量指针,触发逃逸
|
||||
func newPerson(name string, age int) *Person {
|
||||
p := &Person{Name: name, Age: age} // p 逃逸至堆
|
||||
return p
|
||||
}
|
||||
|
||||
func main() {
|
||||
p := newPerson("Alice", 25)
|
||||
fmt.Printf("局部变量指针返回:%+v\n", *p)
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type Person3 struct {
|
||||
Name string
|
||||
Age int
|
||||
}
|
||||
|
||||
// sliceEscape 切片元素为指针,且切片被返回,元素逃逸
|
||||
func sliceEscape() []*Person3 {
|
||||
var people []*Person3
|
||||
for i := 0; i < 3; i++ {
|
||||
person := &Person3{Name: fmt.Sprintf("Person3%d", i), Age: 20 + i}
|
||||
people = append(people, person) // person 逃逸
|
||||
}
|
||||
return people
|
||||
}
|
||||
|
||||
// dynamicSlice 切片动态扩容,底层数组逃逸
|
||||
func dynamicSlice() []int {
|
||||
s := make([]int, 0) // 编译期无法确定最终容量
|
||||
for i := 0; i < 100; i++ {
|
||||
s = append(s, i) // 扩容时底层数组逃逸
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// mapEscape 映射值为指针,值逃逸
|
||||
func mapEscape() map[string]*Person3 {
|
||||
m := make(map[string]*Person3)
|
||||
m["alice"] = &Person3{Name: "Alice", Age: 25} // 指针值逃逸
|
||||
return m
|
||||
}
|
||||
|
||||
func main() {
|
||||
people := sliceEscape()
|
||||
fmt.Printf("切片元素逃逸:%d个Person\n", len(people))
|
||||
|
||||
s := dynamicSlice()
|
||||
fmt.Printf("切片扩容逃逸:len=%d, cap=%d\n", len(s), cap(s))
|
||||
|
||||
m := mapEscape()
|
||||
fmt.Printf("映射值逃逸:%+v\n", *m["alice"])
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
[2026-05-06 14:07:08] 服务启动成功
|
||||
[2026-05-06 14:07:27] 服务启动成功
|
||||
@@ -0,0 +1 @@
|
||||
{"env":"production","version":"1.0.0"}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
Go文件操作最佳实践
|
||||
@@ -0,0 +1 @@
|
||||
测试embed文件
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
test文件内容测试111111111111
|
||||
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// 标准资源释放:打开文件后立即defer关闭,检查关闭错误
|
||||
func main() {
|
||||
// 打开文件
|
||||
file, err := os.Open("file/test.txt")
|
||||
// 核心:必须处理打开错误
|
||||
if err != nil {
|
||||
panic("文件打开失败:" + err.Error())
|
||||
}
|
||||
// 核心:defer延迟关闭,函数退出前自动释放句柄
|
||||
defer func() {
|
||||
// 关闭文件时检查错误,释放资源同时捕获异常
|
||||
if closeErr := file.Close(); closeErr != nil {
|
||||
panic("文件关闭失败:" + closeErr.Error())
|
||||
}
|
||||
}()
|
||||
|
||||
// 执行业务读写逻辑
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func fileOperation() error {
|
||||
// 1. 打开文件错误处理
|
||||
file, err := os.OpenFile("file/data.log", os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
// 包装错误,添加上下文
|
||||
return fmt.Errorf("打开文件失败: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if closeErr := file.Close(); closeErr != nil {
|
||||
fmt.Printf("警告:文件关闭异常: %v\n", closeErr)
|
||||
}
|
||||
}()
|
||||
|
||||
// 2. 写入数据错误处理
|
||||
content := []byte("Go文件操作最佳实践")
|
||||
n, err := io.WriteString(file, string(content))
|
||||
if err != nil {
|
||||
return fmt.Errorf("文件写入失败: %w", err)
|
||||
}
|
||||
if n != len(content) {
|
||||
return errors.New("文件写入不完整")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := fileOperation(); err != nil {
|
||||
fmt.Printf("文件操作执行失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("文件操作成功")
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 小文件一次性读写:代码简洁,无资源泄漏风险
|
||||
func smallFileRW() {
|
||||
path := "file/config.json"
|
||||
content := []byte(`{"env":"production","version":"1.0.0"}`)
|
||||
|
||||
// 一次性写入:自动创建文件、覆盖内容、关闭文件
|
||||
err := os.WriteFile(path, content, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("写入失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 一次性读取:自动打开、读取、关闭文件
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
fmt.Printf("读取失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("文件内容: %s\n", string(data))
|
||||
}
|
||||
|
||||
func main() {
|
||||
smallFileRW()
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// 大文件缓冲读写:逐行处理,内存占用恒定
|
||||
func bigFileRW() {
|
||||
// 打开源文件
|
||||
srcFile, err := os.Open("file/large_file.txt")
|
||||
if err != nil {
|
||||
fmt.Printf("打开源文件失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer srcFile.Close()
|
||||
|
||||
// 创建目标文件
|
||||
dstFile, err := os.Create("file/copy_large_file.txt")
|
||||
if err != nil {
|
||||
fmt.Printf("创建目标文件失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer dstFile.Close()
|
||||
|
||||
// 创建缓冲读写器
|
||||
reader := bufio.NewReader(srcFile)
|
||||
writer := bufio.NewWriter(dstFile)
|
||||
defer func() {
|
||||
// 刷新缓冲区并检查错误
|
||||
if err := writer.Flush(); err != nil {
|
||||
fmt.Printf("缓冲区刷新失败: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 逐行读取写入
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Printf("读取失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
_, err = writer.WriteString(line)
|
||||
if err != nil {
|
||||
fmt.Printf("写入失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
fmt.Println("大文件拷贝完成")
|
||||
}
|
||||
|
||||
func main() {
|
||||
bigFileRW()
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 文件追加写入:日志场景最佳实践
|
||||
func appendFile() {
|
||||
path := "file/app.log"
|
||||
// 打开模式:O_CREATE(不存在则创建)|O_APPEND(追加)|O_WRONLY(只写)
|
||||
file, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("打开日志文件失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if err := file.Close(); err != nil {
|
||||
fmt.Printf("关闭文件失败: %v\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 写入日志
|
||||
log := fmt.Sprintf("[%s] 服务启动成功\n", time.Now().Format("2006-01-02 15:04:05"))
|
||||
_, err = file.WriteString(log)
|
||||
if err != nil {
|
||||
fmt.Printf("写入日志失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("日志追加成功")
|
||||
}
|
||||
|
||||
func main() {
|
||||
appendFile()
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func getFileInfo() {
|
||||
path := "file/test.txt"
|
||||
// 获取文件信息
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
// 判断文件是否不存在
|
||||
if os.IsNotExist(err) {
|
||||
fmt.Println("文件不存在")
|
||||
return
|
||||
}
|
||||
fmt.Printf("获取文件信息失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 输出元数据
|
||||
fmt.Printf("文件名: %s\n", info.Name())
|
||||
fmt.Printf("文件大小: %d 字节\n", info.Size())
|
||||
fmt.Printf("文件权限: %s\n", info.Mode().String())
|
||||
fmt.Printf("修改时间: %s\n", info.ModTime().Format("2006-01-02"))
|
||||
fmt.Printf("是否为目录: %t\n", info.IsDir())
|
||||
}
|
||||
|
||||
func main() {
|
||||
getFileInfo()
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//go:embed file/embed.txt
|
||||
var embedFile embed.FS
|
||||
|
||||
func embedFileDemo() {
|
||||
// 读取编译嵌入的文件,无磁盘IO操作
|
||||
data, err := embedFile.ReadFile("file/embed.txt")
|
||||
if err != nil {
|
||||
fmt.Printf("读取嵌入文件失败: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Printf("嵌入文件内容: %s\n", string(data))
|
||||
}
|
||||
|
||||
func main() {
|
||||
embedFileDemo()
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 方式1:变量绑定
|
||||
var name string
|
||||
flag.StringVar(&name, "name", "guest", "用户名")
|
||||
|
||||
// 方式2:指针获取
|
||||
age := flag.Int("age", 18, "用户年龄")
|
||||
enable := flag.Bool("enable", false, "是否启用功能")
|
||||
|
||||
// 解析命令行参数
|
||||
flag.Parse()
|
||||
|
||||
// 输出结果
|
||||
fmt.Printf("用户名:%s\n", name)
|
||||
fmt.Printf("年龄:%d\n", *age)
|
||||
fmt.Printf("功能启用:%t\n", *enable)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
port := flag.Int("port", 8080, "服务端口")
|
||||
host := flag.String("host", "localhost", "服务绑定主机")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// 环境变量仅覆盖未手动传参的配置
|
||||
if !flag.IsSet("port") {
|
||||
if envPort := os.Getenv("SERVICE_PORT"); envPort != "" {
|
||||
if p, err := strconv.Atoi(envPort); err == nil {
|
||||
*port = p
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !flag.IsSet("host") {
|
||||
if envHost := os.Getenv("SERVICE_HOST"); envHost != "" {
|
||||
*host = envHost
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("最终生效配置:主机=%s,端口=%d\n", *host, *port)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
port := flag.Int("port", 8080, "服务监听端口")
|
||||
config := flag.String("config", "./config.yaml", "配置文件路径")
|
||||
|
||||
// 重写全局帮助信息
|
||||
flag.Usage = func() {
|
||||
_, err := fmt.Fprintf(flag.CommandLine.Output(), "=== 服务启动工具 V1.0 ===\n\n")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = fmt.Fprintf(flag.CommandLine.Output(), "用法:%s [可选参数]\n\n", os.Args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = fmt.Fprintf(flag.CommandLine.Output(), "参数说明:\n")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
flag.PrintDefaults()
|
||||
_, err = fmt.Fprintf(flag.CommandLine.Output(), "\n使用示例:\n")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_, err = fmt.Fprintf(flag.CommandLine.Output(), " %s -port=8080 -config=app.yaml\n", os.Args[0])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
flag.Parse()
|
||||
|
||||
fmt.Printf("服务启动成功,端口:%d,配置文件:%s\n", *port, *config)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// PortRange 自定义端口范围类型,支持 min-max 格式
|
||||
type PortRange struct {
|
||||
Min int
|
||||
Max int
|
||||
}
|
||||
|
||||
// Set 实现Value接口,完成参数解析和合法性校验
|
||||
func (p *PortRange) Set(s string) error {
|
||||
parts := strings.Split(s, "-")
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("格式错误,端口范围格式必须为 minV-max")
|
||||
}
|
||||
|
||||
var minV, maxV int
|
||||
_, err1 := fmt.Sscanf(parts[0], "%d", &minV)
|
||||
_, err2 := fmt.Sscanf(parts[1], "%d", &maxV)
|
||||
if err1 != nil || err2 != nil {
|
||||
return fmt.Errorf("端口必须为整数")
|
||||
}
|
||||
|
||||
if minV < 1 || minV > 65535 || maxV < 1 || maxV > 65535 || minV > maxV {
|
||||
return fmt.Errorf("端口范围必须在1-65535之间,且min <= max")
|
||||
}
|
||||
|
||||
p.Min = minV
|
||||
p.Max = maxV
|
||||
return nil
|
||||
}
|
||||
|
||||
// String 实现Value接口,定义默认值与字符串展示
|
||||
func (p *PortRange) String() string {
|
||||
if p.Min == 0 && p.Max == 0 {
|
||||
return "1-65535"
|
||||
}
|
||||
return fmt.Sprintf("%d-%d", p.Min, p.Max)
|
||||
}
|
||||
|
||||
// Get 实现Getter接口,返回原始类型,支持类型断言
|
||||
func (p *PortRange) Get() any {
|
||||
return *p
|
||||
}
|
||||
|
||||
func main() {
|
||||
var portRange PortRange
|
||||
flag.Var(&portRange, "port-range", "端口范围,格式:min-max")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
fmt.Printf("有效端口范围:[%d, %d]\n", portRange.Min, portRange.Max)
|
||||
fmt.Printf("类型安全取值:%#v\n", portRange.Get().(PortRange))
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
port := flag.Int("port", 8080, "服务端口")
|
||||
flag.Parse()
|
||||
|
||||
fmt.Println("动态修改前端口:", *port)
|
||||
|
||||
// 动态覆盖参数值
|
||||
if err := flag.Set("port", "9090"); err != nil {
|
||||
fmt.Println("参数设置失败:", err)
|
||||
}
|
||||
|
||||
fmt.Println("动态修改后端口:", *port)
|
||||
|
||||
// 精准查询参数元信息
|
||||
if f := flag.Lookup("port"); f != nil {
|
||||
fmt.Printf("参数详情:名称=%s, 当前值=%s, 默认值=%s, 用途=%s\n",
|
||||
f.Name, f.Value.String(), f.DefValue, f.Usage)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Environment 运行环境枚举类型
|
||||
type Environment string
|
||||
|
||||
const (
|
||||
EnvDev Environment = "dev"
|
||||
EnvTest Environment = "test"
|
||||
EnvProd Environment = "prod"
|
||||
)
|
||||
|
||||
// 合法环境白名单
|
||||
var validEnvs = map[Environment]bool{
|
||||
EnvDev: true,
|
||||
EnvTest: true,
|
||||
EnvProd: true,
|
||||
}
|
||||
|
||||
func (e *Environment) Set(s string) error {
|
||||
env := Environment(strings.ToLower(s))
|
||||
if !validEnvs[env] {
|
||||
return fmt.Errorf("无效运行环境,仅支持 dev/test/prod")
|
||||
}
|
||||
*e = env
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Environment) String() string {
|
||||
if *e == "" {
|
||||
return string(EnvDev)
|
||||
}
|
||||
return string(*e)
|
||||
}
|
||||
|
||||
func (e *Environment) Get() any {
|
||||
return *e
|
||||
}
|
||||
|
||||
func main() {
|
||||
var env Environment = EnvDev // 默认值为 dev
|
||||
flag.Var(&env, "env", "运行环境:dev/test/prod")
|
||||
flag.Parse()
|
||||
|
||||
fmt.Printf("当前运行环境:%s\n", env)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 子命令标准模式:手动捕获错误,不自动退出
|
||||
fs := flag.NewFlagSet("demo", flag.ContinueOnError)
|
||||
num := fs.Int("num", 0, "数字参数")
|
||||
|
||||
// 传入非法参数
|
||||
err := fs.Parse([]string{"-num=abc"})
|
||||
if err != nil {
|
||||
fmt.Printf("参数解析失败:%v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println("参数值:", *num)
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 普通函数参数,传参即触发回调
|
||||
flag.Func("msg", "自定义消息参数", func(s string) error {
|
||||
fmt.Println("接收参数消息:", s)
|
||||
return nil
|
||||
})
|
||||
|
||||
// 布尔函数参数,无值传参触发
|
||||
flag.BoolFunc("debug", "开启调试模式", func(s string) error {
|
||||
fmt.Println("调试模式状态:", s)
|
||||
return nil
|
||||
})
|
||||
|
||||
flag.Parse()
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// FilePath 带合法性校验的文件路径类型
|
||||
type FilePath string
|
||||
|
||||
func (f *FilePath) Set(s string) error {
|
||||
// 解析绝对路径
|
||||
absPath, err := filepath.Abs(s)
|
||||
if err != nil {
|
||||
return fmt.Errorf("路径解析失败:%w", err)
|
||||
}
|
||||
|
||||
// 校验文件存在且为普通文件
|
||||
info, err := os.Stat(absPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return fmt.Errorf("配置文件不存在:%s", absPath)
|
||||
}
|
||||
return fmt.Errorf("文件访问异常:%w", err)
|
||||
}
|
||||
if info.IsDir() {
|
||||
return fmt.Errorf("路径为目录,需传入有效文件路径:%s", absPath)
|
||||
}
|
||||
|
||||
*f = FilePath(absPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FilePath) String() string {
|
||||
return string(*f)
|
||||
}
|
||||
|
||||
func (f *FilePath) Get() any {
|
||||
return string(*f)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var configFile FilePath
|
||||
flag.Var(&configFile, "config", "必填配置文件路径")
|
||||
flag.Parse()
|
||||
|
||||
fmt.Printf("加载配置文件:%s\n", configFile)
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
port := flag.Int("port", 8080, "服务端口")
|
||||
flag.Parse()
|
||||
|
||||
// 获取无标记位置参数
|
||||
args := flag.Args()
|
||||
if len(args) == 0 {
|
||||
fmt.Println("请传入配置文件路径")
|
||||
return
|
||||
}
|
||||
fmt.Printf("端口:%d,配置文件:%s\n", *port, args[0])
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StringSlice2 []string
|
||||
|
||||
func (s *StringSlice2) Set(val string) error {
|
||||
*s = append(*s, val)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *StringSlice2) String() string {
|
||||
return strings.Join(*s, ",")
|
||||
}
|
||||
|
||||
func main() {
|
||||
var files StringSlice2
|
||||
flag.Var(&files, "file", "指定多个配置文件,可重复传参")
|
||||
flag.Parse()
|
||||
fmt.Printf("文件列表:%#v\n", files)
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StringSlice 支持重复传参的字符串切片类型
|
||||
type StringSlice []string
|
||||
|
||||
// Set 实现参数追加逻辑,覆盖原生覆盖问题
|
||||
func (s *StringSlice) Set(value string) error {
|
||||
*s = append(*s, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String 实现字符串展示与零值返回
|
||||
func (s *StringSlice) String() string {
|
||||
if len(*s) == 0 {
|
||||
return "[]"
|
||||
}
|
||||
return strings.Join(*s, ",")
|
||||
}
|
||||
|
||||
// Get 实现Getter接口,返回原始切片类型
|
||||
func (s *StringSlice) Get() any {
|
||||
return []string(*s)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var files StringSlice
|
||||
var tags StringSlice
|
||||
|
||||
flag.Var(&files, "file", "指定配置文件,可重复传参")
|
||||
flag.Var(&tags, "tag", "自定义业务标签,可重复传参")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
fmt.Printf("文件列表:%v (原始类型:%T)\n", files.Get(), files.Get())
|
||||
fmt.Printf("标签列表:%v (原始类型:%T)\n", tags.Get(), tags.Get())
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 初始化子命令FlagSet
|
||||
startCmd := flag.NewFlagSet("start", flag.ContinueOnError)
|
||||
stopCmd := flag.NewFlagSet("stop", flag.ContinueOnError)
|
||||
|
||||
// 子命令私有参数
|
||||
var port int
|
||||
var config string
|
||||
startCmd.IntVar(&port, "port", 8080, "服务监听端口")
|
||||
stopCmd.StringVar(&config, "config", "./config.yaml", "服务配置文件")
|
||||
|
||||
// 校验子命令是否传入
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("可用子命令:start / stop")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 路由分发
|
||||
switch os.Args[1] {
|
||||
case "start":
|
||||
_ = startCmd.Parse(os.Args[2:])
|
||||
fmt.Printf("启动服务成功,端口:%d\n", port)
|
||||
case "stop":
|
||||
_ = stopCmd.Parse(os.Args[2:])
|
||||
fmt.Printf("停止服务,配置文件:%s\n", config)
|
||||
default:
|
||||
fmt.Printf("未知子命令:%s\n", os.Args[1])
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// GlobalOptions 全局公共参数,所有子命令共享
|
||||
type GlobalOptions struct {
|
||||
Verbose bool // 详细日志模式
|
||||
Config string // 全局配置文件
|
||||
}
|
||||
|
||||
// SubCommand 通用子命令抽象结构体
|
||||
type SubCommand struct {
|
||||
Name string
|
||||
Description string
|
||||
Flags *flag.FlagSet
|
||||
Run func([]string, GlobalOptions) error
|
||||
}
|
||||
|
||||
// 提前声明变量,避免初始化循环引用
|
||||
var commands map[string]*SubCommand
|
||||
|
||||
// init 初始化子命令,将 FlagSet 的绑定与执行逻辑分离
|
||||
func init() {
|
||||
// --- 初始化 build 命令 ---
|
||||
buildCmd := &SubCommand{
|
||||
Name: "build",
|
||||
Description: "编译构建项目",
|
||||
Flags: flag.NewFlagSet("build", flag.ContinueOnError),
|
||||
}
|
||||
var buildOutput string
|
||||
// 在外部绑定参数,而不是在 Run 里面重新创建 FlagSet
|
||||
buildCmd.Flags.StringVar(&buildOutput, "output", "./dist", "项目构建输出目录")
|
||||
buildCmd.Run = func(args []string, global GlobalOptions) error {
|
||||
if err := buildCmd.Flags.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("[构建成功] 输出目录:%s,全局详细日志:%t,全局配置:%s\n",
|
||||
buildOutput, global.Verbose, global.Config)
|
||||
return nil
|
||||
}
|
||||
|
||||
// --- 初始化 deploy 命令 ---
|
||||
deployCmd := &SubCommand{
|
||||
Name: "deploy",
|
||||
Description: "部署项目到环境",
|
||||
Flags: flag.NewFlagSet("deploy", flag.ContinueOnError),
|
||||
}
|
||||
var deployEnv string
|
||||
deployCmd.Flags.StringVar(&deployEnv, "env", "dev", "部署环境 dev/test/prod")
|
||||
deployCmd.Run = func(args []string, global GlobalOptions) error {
|
||||
if err := deployCmd.Flags.Parse(args); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("[部署成功] 目标环境:%s,全局配置文件:%s\n",
|
||||
deployEnv, global.Config)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 注册子命令
|
||||
commands = map[string]*SubCommand{
|
||||
"build": buildCmd,
|
||||
"deploy": deployCmd,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 1. 初始化全局参数 FlagSet(独立隔离)
|
||||
var global GlobalOptions
|
||||
globalFlags := flag.NewFlagSet("global", flag.ContinueOnError)
|
||||
globalFlags.BoolVar(&global.Verbose, "verbose", false, "启用详细日志输出")
|
||||
globalFlags.StringVar(&global.Config, "config", "./config.yaml", "全局配置文件路径")
|
||||
|
||||
// 2. 解析全局参数
|
||||
// 注意:flag 库遇到第一个非 Flag 参数(即子命令名称)就会停止解析
|
||||
if err := globalFlags.Parse(os.Args[1:]); err != nil {
|
||||
if err == flag.ErrHelp {
|
||||
printHelp(globalFlags)
|
||||
return
|
||||
}
|
||||
fmt.Println("全局参数解析失败:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 3. 获取剩余参数(子命令 + 子命令参数)
|
||||
remainingArgs := globalFlags.Args()
|
||||
if len(remainingArgs) == 0 {
|
||||
printHelp(globalFlags)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 4. 路由分发执行子命令
|
||||
cmdName := remainingArgs[0]
|
||||
if cmd, exists := commands[cmdName]; exists {
|
||||
if err := cmd.Run(remainingArgs[1:], global); err != nil {
|
||||
if err == flag.ErrHelp {
|
||||
// 当子命令触发 -h 时,打印该子命令专属的帮助信息
|
||||
fmt.Printf("\n===== 子命令 [%s] 帮助信息 =====\n", cmd.Name)
|
||||
fmt.Printf("说明:%s\n\n用法:%s [选项]\n\n选项:\n", cmd.Description, cmd.Name)
|
||||
cmd.Flags.PrintDefaults()
|
||||
return
|
||||
}
|
||||
fmt.Printf("子命令执行失败:%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("❌ 未知子命令:%s\n", cmdName)
|
||||
printHelp(globalFlags)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// printHelp 统一打印全局帮助信息
|
||||
func printHelp(globalFlags *flag.FlagSet) {
|
||||
fmt.Println("===== 项目工程化 CLI 工具 =====")
|
||||
fmt.Println("用法:tool [全局选项] <子命令> [子命令选项]")
|
||||
fmt.Println("\n全局选项:")
|
||||
globalFlags.PrintDefaults()
|
||||
|
||||
fmt.Println("\n可用子命令:")
|
||||
for _, cmd := range commands {
|
||||
fmt.Printf(" %-10s %s\n", cmd.Name, cmd.Description)
|
||||
}
|
||||
fmt.Println("\n提示:使用 'tool <子命令> -h' 查看子命令详细帮助")
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// CustomTime 自定义时间类型,复用标准库文本序列化接口
|
||||
type CustomTime time.Time
|
||||
|
||||
// MarshalText 实现文本序列化(对应默认值展示)
|
||||
func (t CustomTime) MarshalText() ([]byte, error) {
|
||||
return []byte(time.Time(t).Format(time.RFC3339)), nil
|
||||
}
|
||||
|
||||
// UnmarshalText 实现文本反序列化(参数解析核心)
|
||||
func (t *CustomTime) UnmarshalText(b []byte) error {
|
||||
parseTime, err := time.Parse(time.RFC3339, string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*t = CustomTime(parseTime)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 枚举类型示例:自定义运行模式
|
||||
type RunMode string
|
||||
|
||||
const (
|
||||
ModeDev RunMode = "dev"
|
||||
ModeProd RunMode = "prod"
|
||||
)
|
||||
|
||||
func (m RunMode) MarshalText() ([]byte, error) {
|
||||
return []byte(m), nil
|
||||
}
|
||||
|
||||
func (m *RunMode) UnmarshalText(b []byte) error {
|
||||
val := RunMode(b)
|
||||
if val != ModeDev && val != ModeProd {
|
||||
return fmt.Errorf("非法运行模式,仅支持 dev/prod")
|
||||
}
|
||||
*m = val
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 1. 自定义时间参数 TextVar 绑定
|
||||
var execTime CustomTime
|
||||
defaultTime := CustomTime(time.Now())
|
||||
flag.TextVar(&execTime, "exec-time", defaultTime, "执行时间(RFC3339格式)")
|
||||
|
||||
// 2. 自定义枚举参数 TextVar 绑定
|
||||
var runMode RunMode
|
||||
flag.TextVar(&runMode, "mode", ModeDev, "运行模式 dev/prod")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
fmt.Printf("运行模式:%s\n", runMode)
|
||||
fmt.Printf("执行时间:%s\n", time.Time(execTime).Format(time.RFC3339))
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
// PortValue 自定义端口类型,实现flag.Value接口
|
||||
type PortValue int
|
||||
|
||||
func (p *PortValue) Set(s string) error {
|
||||
var port int
|
||||
_, err := fmt.Sscanf(s, "%d", &port)
|
||||
if err != nil || port < 1 || port > 65535 {
|
||||
return fmt.Errorf("非法端口,必须为1-65535整数")
|
||||
}
|
||||
*p = PortValue(port)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *PortValue) String() string {
|
||||
return fmt.Sprintf("%d", *p)
|
||||
}
|
||||
|
||||
// EnvValue 枚举环境校验类型
|
||||
type EnvValue string
|
||||
|
||||
func (e *EnvValue) Set(s string) error {
|
||||
if s != "dev" && s != "prod" {
|
||||
return fmt.Errorf("仅支持 dev/prod 环境")
|
||||
}
|
||||
*e = EnvValue(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *EnvValue) String() string {
|
||||
return string(*e)
|
||||
}
|
||||
|
||||
func main() {
|
||||
var port PortValue
|
||||
var env EnvValue
|
||||
flag.Var(&port, "port", "服务端口 1-65535")
|
||||
flag.Var(&env, "env", "运行环境 dev/prod")
|
||||
config := flag.String("config", "", "配置文件路径(必填)")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
// 后置非空校验
|
||||
if *config == "" {
|
||||
fmt.Println("错误:config 参数不能为空")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("环境:%s,端口:%d,配置:%s\n", env, port, *config)
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 定义基础参数
|
||||
flag.Int("port", 8080, "服务监听端口")
|
||||
flag.String("host", "localhost", "服务绑定主机")
|
||||
flag.Bool("debug", false, "开启调试模式")
|
||||
|
||||
// 解析参数
|
||||
flag.Parse()
|
||||
|
||||
fmt.Println("=== 所有已定义参数(含默认值)===")
|
||||
flag.VisitAll(func(f *flag.Flag) {
|
||||
fmt.Printf("参数名:%-10s 默认值:%-10s 说明:%s\n", f.Name, f.DefValue, f.Usage)
|
||||
})
|
||||
|
||||
fmt.Println("\n=== 用户主动传入的参数===")
|
||||
flag.Visit(func(f *flag.Flag) {
|
||||
fmt.Printf("参数名:%-10s 当前值:%-10s 已修改:%v\n", f.Name, f.Value.String(), f.Value.String() != f.DefValue)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// 定义函数类型(计算函数)
|
||||
type Calculator func(int, int) int
|
||||
|
||||
// 实现加法
|
||||
func add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// 实现乘法
|
||||
func multiply(a, b int) int {
|
||||
return a * b
|
||||
}
|
||||
|
||||
// 函数作为参数(策略模式)
|
||||
func calculate(a, b int, op Calculator) int {
|
||||
return op(a, b)
|
||||
}
|
||||
|
||||
// 高阶函数:返回函数
|
||||
func apply(f func(int) int, x int) int {
|
||||
return f(x)
|
||||
}
|
||||
|
||||
// 闭包:捕获外部变量
|
||||
func makeAdder(base int) Calculator {
|
||||
// 匿名函数捕获base
|
||||
return func(a, b int) int {
|
||||
return a + b + base
|
||||
}
|
||||
}
|
||||
|
||||
// 闭包:计数器
|
||||
func counter() func() int {
|
||||
count := 0
|
||||
return func() int {
|
||||
count++
|
||||
return count
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 函数作为变量
|
||||
var calc Calculator = add
|
||||
fmt.Println("3+5=", calc(3, 5)) // 8
|
||||
|
||||
// 函数作为参数
|
||||
fmt.Println("2*4=", calculate(2, 4, multiply)) // 8
|
||||
|
||||
// 高阶函数调用
|
||||
double := func(x int) int { return x * 2 }
|
||||
fmt.Printf("apply(4, double)=%d\n", apply(double, 4)) // 输出8
|
||||
|
||||
// 闭包示例
|
||||
adder := makeAdder(10) // 基础值10
|
||||
fmt.Println("1+2+10=", adder(1, 2)) // 13
|
||||
|
||||
// 计数器闭包
|
||||
c1 := counter()
|
||||
fmt.Printf("c1第1次: %d\n", c1()) // 输出1
|
||||
fmt.Printf("c1第2次: %d\n", c1()) // 输出2
|
||||
|
||||
c2 := counter()
|
||||
fmt.Printf("c2第1次: %d\n", c2()) // 输出1(c1与c2的count独立)
|
||||
|
||||
// 错误示例
|
||||
// var f func()
|
||||
// f() // 报错:panic: runtime error: invalid memory address or nil pointer dereference
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof" // 注册pprof HTTP端点
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/trace"
|
||||
"time"
|
||||
)
|
||||
|
||||
// simulateAlloc 模拟高频内存分配
|
||||
func simulateAlloc() {
|
||||
for {
|
||||
_ = make([]byte, 1*1024*1024) // 每次分配1MB
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 1. 启动trace追踪,输出到文件
|
||||
f, err := os.Create("gc_trace.out")
|
||||
if err != nil {
|
||||
log.Fatalf("创建trace文件失败: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
trace.Start(f)
|
||||
defer trace.Stop()
|
||||
|
||||
// 2. 启动pprof HTTP服务(端口6060)
|
||||
go func() {
|
||||
log.Println("pprof服务启动: http://localhost:6060/debug/pprof/")
|
||||
log.Fatal(http.ListenAndServe(":6060", nil))
|
||||
}()
|
||||
|
||||
// 3. 模拟内存分配压力
|
||||
go simulateAlloc()
|
||||
|
||||
// 4. 运行30秒,积累诊断数据
|
||||
log.Println("程序将运行30秒,可访问pprof分析GC...")
|
||||
time.Sleep(30 * time.Second)
|
||||
|
||||
// 5. 打印最终GC状态
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
log.Printf("运行结束,GC总次数: %d,累计STW时长: %d ms", stats.NumGC, stats.PauseTotalNs/1e6)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// printGCStats 打印GC核心统计信息
|
||||
func printGCStats() {
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
|
||||
fmt.Println("===== GC 统计信息 =====")
|
||||
fmt.Printf("当前堆分配内存: %d MB\n", stats.Alloc/1024/1024) // 等价于HeapAlloc
|
||||
fmt.Printf("存活使用堆内存: %d MB\n", stats.HeapInuse/1024/1024) // 真正的存活内存
|
||||
fmt.Printf("空闲待回收内存: %d MB\n", stats.HeapIdle/1024/1024) // 垃圾内存
|
||||
fmt.Printf("累计 STW 时长: %.3f ms\n", float64(stats.PauseTotalNs)/1e6)
|
||||
fmt.Printf("GC 总次数: %d\n", stats.NumGC)
|
||||
fmt.Printf("最近一次 STW 时长: %.3f μs\n", float64(stats.PauseNs[(stats.NumGC+255)%256])/1e3)
|
||||
fmt.Printf("下一次GC触发阈值: %d MB\n", stats.NextGC/1024/1024) // 下次GC的内存阈值
|
||||
fmt.Println("=======================\n")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 后台定时打印GC状态
|
||||
go func() {
|
||||
for {
|
||||
printGCStats()
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
}()
|
||||
|
||||
// 模拟高频内存分配,触发GC
|
||||
data := make([][]byte, 0)
|
||||
for i := 0; i < 100; i++ {
|
||||
data = append(data, make([]byte, 10*1024*1024)) // 每次分配10MB
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
|
||||
// 阻塞程序退出
|
||||
select {}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// badFunc 局部大对象返回,触发逃逸
|
||||
func badFunc() []byte {
|
||||
buf := make([]byte, 1024*1024) // 1MB对象
|
||||
return buf
|
||||
}
|
||||
|
||||
// goodFunc 接收外部切片,复用内存,避免逃逸
|
||||
func goodFunc(buf []byte) {
|
||||
for i := range buf {
|
||||
buf[i] = byte(i % 256)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var stats runtime.MemStats
|
||||
|
||||
// 测试逃逸场景
|
||||
runtime.GC()
|
||||
for i := 0; i < 50; i++ {
|
||||
_ = badFunc()
|
||||
}
|
||||
runtime.ReadMemStats(&stats)
|
||||
fmt.Printf("逃逸场景:堆分配%d MB,GC次数%d\n", stats.HeapAlloc/1024/1024, stats.NumGC)
|
||||
|
||||
// 测试避免逃逸场景
|
||||
runtime.GC()
|
||||
buf := make([]byte, 1024*1024) // 一次分配,复用
|
||||
for i := 0; i < 50; i++ {
|
||||
goodFunc(buf)
|
||||
}
|
||||
runtime.ReadMemStats(&stats)
|
||||
fmt.Printf("避免逃逸:堆分配%d MB,GC次数%d\n", stats.HeapAlloc/1024/1024, stats.NumGC)
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// leakSlice 全局切片,持续追加数据导致内存泄漏
|
||||
var leakSlice [][]byte
|
||||
|
||||
// generateLeak 模拟内存泄漏
|
||||
func generateLeak() {
|
||||
for {
|
||||
// 每次分配10MB,追加到全局切片(永久引用,无法回收)
|
||||
leakSlice = append(leakSlice, make([]byte, 10*1024*1024))
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// 打印当前内存状态
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
fmt.Printf("当前堆内存:%d MB,GC次数:%d\n", stats.HeapAlloc/1024/1024, stats.NumGC)
|
||||
}
|
||||
}
|
||||
|
||||
func generateLeak1() {
|
||||
var maxMem uint64 = 500 // 最大允许占用500MB内存
|
||||
for {
|
||||
leakSlice = append(leakSlice, make([]byte, 10*1024*1024))
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
curMem := stats.HeapAlloc / 1024 / 1024
|
||||
fmt.Printf("当前堆内存:%d MB,GC次数:%d\n", curMem, stats.NumGC)
|
||||
|
||||
// ✅ 核心修复:内存超限后,置空全局切片,切断引用
|
||||
if curMem >= maxMem {
|
||||
leakSlice = nil // 关键一行!置空后,原切片的所有[]byte都失去引用
|
||||
runtime.GC() // 手动触发GC,立刻回收内存
|
||||
fmt.Println("=== 内存超限,已释放所有内存 ===")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func generateLeak2() {
|
||||
maxLen := 20 // 最多保留20个10MB切片,总内存上限200MB
|
||||
for {
|
||||
leakSlice = append(leakSlice, make([]byte, 10*1024*1024))
|
||||
// ✅ 核心修复:超过最大长度,删除头部旧数据,释放引用
|
||||
if len(leakSlice) > maxLen {
|
||||
leakSlice = leakSlice[1:] // 切片切片,丢弃第一个元素,GC会回收它
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
fmt.Printf("当前堆内存:%d MB,GC次数:%d,切片长度:%d\n", stats.HeapAlloc/1024/1024, stats.NumGC, len(leakSlice))
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 定义10MB切片的对象池
|
||||
var needBytePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 10*1024*1024) // 每次创建10MB切片
|
||||
},
|
||||
}
|
||||
|
||||
// 无内存泄漏!复用内存,不持有永久引用
|
||||
func generateLeak3() {
|
||||
for {
|
||||
// 从池子里拿切片,复用内存
|
||||
buf := needBytePool.Get().([]byte)
|
||||
// 模拟业务使用
|
||||
_ = buf
|
||||
// 使用完放回池子,供后续复用,释放引用
|
||||
needBytePool.Put(buf[:0])
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
fmt.Printf("当前堆内存:%d MB,GC次数:%d\n", stats.HeapAlloc/1024/1024, stats.NumGC)
|
||||
}
|
||||
}
|
||||
func main() {
|
||||
// 启动pprof服务
|
||||
go func() {
|
||||
log.Println("pprof服务启动:http://localhost:6060/debug/pprof/")
|
||||
log.Fatal(http.ListenAndServe(":6060", nil))
|
||||
}()
|
||||
|
||||
// 模拟内存泄漏
|
||||
go generateLeak3()
|
||||
|
||||
// 运行60秒,便于诊断
|
||||
time.Sleep(60 * time.Second)
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
// noPrealloc 未预分配容量,频繁扩容
|
||||
func noPrealloc() {
|
||||
var data []int
|
||||
for i := 0; i < 100000; i++ {
|
||||
data = append(data, i)
|
||||
}
|
||||
}
|
||||
|
||||
// withPrealloc 预分配容量,无扩容
|
||||
func withPrealloc() {
|
||||
data := make([]int, 0, 100000) // 预分配容量100000
|
||||
for i := 0; i < 100000; i++ {
|
||||
data = append(data, i)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
var stats runtime.MemStats
|
||||
|
||||
// 测试未预分配
|
||||
runtime.GC()
|
||||
start := time.Now()
|
||||
noPrealloc()
|
||||
dur1 := time.Since(start)
|
||||
runtime.ReadMemStats(&stats)
|
||||
fmt.Printf("未预分配:耗时%v,堆分配%d KB,GC次数%d\n", dur1, stats.HeapAlloc/1024, stats.NumGC)
|
||||
|
||||
// 测试预分配
|
||||
runtime.GC()
|
||||
start = time.Now()
|
||||
withPrealloc()
|
||||
dur2 := time.Since(start)
|
||||
runtime.ReadMemStats(&stats)
|
||||
fmt.Printf("预分配:耗时%v,堆分配%d KB,GC次数%d\n", dur2, stats.HeapAlloc/1024, stats.NumGC)
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 初始化1KB字节切片对象池
|
||||
var bytePool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, 1024)
|
||||
},
|
||||
}
|
||||
|
||||
// 打印GC统计,衔接你之前的知识
|
||||
func printGCStatsInfo(title string) {
|
||||
var stats runtime.MemStats
|
||||
runtime.ReadMemStats(&stats)
|
||||
fmt.Printf("[%s] GC次数: %d, 存活堆内存: %d KB\n", title, stats.NumGC, stats.HeapAlloc/1024)
|
||||
}
|
||||
|
||||
// withoutPool 不使用对象池,频繁分配新内存
|
||||
// 增加参数 []byte 模拟真实业务使用,禁止编译器消除代码
|
||||
func withoutPool() time.Duration {
|
||||
start := time.Now()
|
||||
var temp []byte // 用于承接数据,避免死代码
|
||||
for i := 0; i < 1000000; i++ { // 循环次数提升10倍,让耗时可被精准统计
|
||||
buf := make([]byte, 1024)
|
||||
temp = append(temp, buf...) // 模拟真实使用切片,编译器无法优化
|
||||
}
|
||||
_ = temp
|
||||
return time.Since(start)
|
||||
}
|
||||
|
||||
// withPool 使用对象池复用内存
|
||||
func withPool() time.Duration {
|
||||
start := time.Now()
|
||||
var temp []byte // 用于承接数据,避免死代码
|
||||
for i := 0; i < 1000000; i++ { // 同步提升循环次数
|
||||
buf := bytePool.Get().([]byte)
|
||||
temp = append(temp, buf...) // 模拟真实使用切片
|
||||
bytePool.Put(buf[:0]) // 正确放回,保留你的最佳实践
|
||||
}
|
||||
_ = temp
|
||||
return time.Since(start)
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 预热并清空GC状态
|
||||
runtime.GC()
|
||||
printGCStatsInfo("初始化")
|
||||
|
||||
// 测试无对象池
|
||||
dur1 := withoutPool()
|
||||
printGCStatsInfo("无对象池后")
|
||||
runtime.GC() // 强制GC,消除前序影响
|
||||
|
||||
// 测试有对象池
|
||||
dur2 := withPool()
|
||||
printGCStatsInfo("有对象池后")
|
||||
|
||||
// 增加分母非0判断,避免-Inf%
|
||||
var rate float64
|
||||
if dur1 > 0 {
|
||||
rate = (1 - float64(dur2)/float64(dur1)) * 100
|
||||
}
|
||||
|
||||
fmt.Printf("\n无对象池耗时: %v\n", dur1)
|
||||
fmt.Printf("有对象池耗时: %v\n", dur2)
|
||||
fmt.Printf("性能提升: %.2f%%\n", rate)
|
||||
}
|
||||
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user