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

本期结束~

相关推荐
TDengine (老段)8 分钟前
TDengine 语言连接器(Go)
大数据·数据库·物联网·golang·时序数据库·tdengine·iotdb
JZC_xiaozhong11 分钟前
制造企业如何通过实现数据统一?
大数据·spring boot·后端·制造·mdm·主数据管理·数据集成与应用集成
流云一号15 分钟前
Python实现贪吃蛇二
开发语言·python
ππ记录26 分钟前
java面试题带答案2025最新整理
java·开发语言
PHASELESS41128 分钟前
Java栈与队列深度解析:结构、实现与应用指南
java·开发语言·算法
uhakadotcom1 小时前
Python中orjson、json、json5三大JSON库简单对比与实用教程
后端·面试·github
SeasonedDriverDG1 小时前
C语言编写的线程池
linux·c语言·开发语言·算法
南雨北斗1 小时前
Docker部署的优缺点
后端
嶂蘅1 小时前
OK!用大白话说清楚设计模式(二)
前端·后端·面试