Go小技巧&易错点100例(二十五)

本期分享

1. 使用atomic包实现无锁并发控制

2. Gin框架的中间件机制

3. 搞懂nil切片和空切片


使用atomic包实现无锁并发控制

sync/atomic包提供了原子操作,用于在多goroutine环境下安全地操作共享变量,避免使用锁带来的性能开销。

代码示例:

go 复制代码
package main

import (
    "fmt"
    "sync"
    "sync/atomic"
)

var counter int64

func increment(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 1000; i++ {
        atomic.AddInt64(&counter, 1)
    }
}

func main() {
    var wg sync.WaitGroup
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go increment(&wg)
    }
    wg.Wait()
    fmt.Println("Final Counter:", counter) // 应为100 * 1000 = 100000
}

Gin框架的中间件机制

Gin是一个高性能的Go Web框架,支持中间件机制。通过自定义中间件,可以实现日志记录、认证、限流等功能,提高代码的模块化和可维护性。

代码示例:

go 复制代码
package main

import (
    "github.com/gin-gonic/gin"
    "log"
    "time"
)

// 自定义日志中间件
func LoggerMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        start := time.Now()
        // 处理请求
        c.Next()
        // 请求结束后记录日志
        duration := time.Since(start)
        log.Printf("Request %s %s took %v", c.Request.Method, c.Request.URL.Path, duration)
    }
}

func main() {
    r := gin.New()
    // 使用自定义中间件
    r.Use(LoggerMiddleware())

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.Run(":8080")
}

搞懂nil切片和空切片

在Go语言中,nil切片和空切片是两个不同的概念,但它们有一些共同点和不同点。以下是它们之间的详细比较:

相同点

1)长度相同nil切片和空切片的长度都是0。这意味着它们都不包含任何元素。

2)零值 :在声明切片变量但未初始化时,它的默认值是nil

不同点

1)底层指针nil切片没有底层数组,其指针部分(即数组的指针)为nil。 空切片有一个底层数组,但其长度和容量都为0,指针部分指向一个有效的零长度数组。

2)追加元素nil切片在追加元素时会分配一个新的底层数组。空切片在追加元素时,如果容量足够,可以直接在现有底层数组上进行操作,否则会分配一个新的底层数组。

3)比较nil切片与nil比较时结果为true。空切片与nil比较时结果为false

总而言之,**nil**切片 :没有底层数组,指针为nil,长度和容量都为0。空切片:有底层数组(但长度为0),指针指向一个有效的零长度数组,长度和容量都为0。

代码示例:

go 复制代码
func TestNilSlice(t *testing.T) {
	var s1 []int
	s2 := make([]int, 0)
	s3 := make([]int, 0)

	fmt.Printf("s1 pointer:%+v \ns2 pointer:%+v \ns3 pointer:%+v, \n",
		*(*reflect.SliceHeader)(unsafe.Pointer(&s1)),
		*(*reflect.SliceHeader)(unsafe.Pointer(&s2)),
		*(*reflect.SliceHeader)(unsafe.Pointer(&s3)))
	fmt.Printf("%v\n",
		(*(*reflect.SliceHeader)(unsafe.Pointer(&s1))).Data == (*(*reflect.SliceHeader)(unsafe.Pointer(&s2))).Data)
	fmt.Printf("%v\n",
		(*(*reflect.SliceHeader)(unsafe.Pointer(&s2))).Data == (*(*reflect.SliceHeader)(unsafe.Pointer(&s3))).Data)
}

输出:

shell 复制代码
s1 pointer:{Data:0 Len:0 Cap:0} 
s2 pointer:{Data:1374390636216 Len:0 Cap:0} 
s3 pointer:{Data:1374390636216 Len:0 Cap:0}, 
false
true

本期结束~

相关推荐
Chenyiax24 分钟前
从 Chat 到 Responses:OpenAI API 抽象为什么变了?
后端
MariaH26 分钟前
Koa和Express的区别
后端
MariaH31 分钟前
Koa框架的使用
后端
luckdewei2 小时前
那个用 passlib 做认证的新同事,上线第一天就把用户密码写进了日志
后端
ping某3 小时前
为什么 Nginx 明明监听了 80,转发后端时却用了 4xxxx 端口?
后端·nginx
JustHappy3 小时前
我汇总了身边朋友的经历才发现,其实第一份实习是最难找的......
前端·后端·面试
uhakadotcom3 小时前
在python 的 工程化架构中 ,什么是 薄包装器层?
后端·面试·github
用户1474853079747 小时前
CodeX使用Skill生成游戏美术和音乐资源,一分钟入门
后端
Melody1238 小时前
用 abort 中断 AI 流式请求,我之前做错了
后端
onething3658 小时前
Spring Boot + Spring AI 从入门到实战:7天转型计划 Day 5 —— SSE 流式输出 + 打字机效果
人工智能·后端·全栈