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

本期结束~

相关推荐
BingoGo2 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php
JaguarJack2 小时前
PHP clone 之后,为什么改副本会影响原对象?
后端·php·服务端
小灰灰搞电子2 小时前
Rust+Slint 实现动态消息提示框源码分享
开发语言·后端·rust
小奏技术3 小时前
10 MB 的 Postman 替代品,启动不到 1 秒
后端
东风破_3 小时前
Text2SQL :用自然语言操作 SQLite 数据库
人工智能·后端
传奇开心果编程4 小时前
【Rust入门知识点学与练】第21课:Trait 进阶 Advanced Traits
开发语言·学习·rust
新时代牛马4 小时前
字符设备驱动完整篇:从 cdev_add、file_operations 到chrdev_open 与排障
开发语言·python
swordbob4 小时前
ReentrantLock 与 AQS 完整学习手册
java·开发语言
白山编程大哥5 小时前
Java OutputStreamWriter 详解:从字符到字节的桥梁
java·开发语言·python
shmily麻瓜小菜鸡6 小时前
JavaScript / TypeScript 易踩坑知识点 —— 异步编程类
开发语言·javascript·typescript