go构建高并发计数器

功能需求

  1. 计数器基本功能:支持递增、递减和获取当前计数值。
  2. 并发安全:确保多个协程同时访问计数器时不会出现数据竞争。
  3. 高效性能:使用适当的同步机制,优化性能。
  4. 重置功能:支持计数器重置为初始值。
  5. 持久化(可选) :支持将计数值持久化到文件或数据库中。
  6. 统计接口:提供API或方法获取计数器访问统计信息(如最大并发量)。

实施计划

  1. 技术调研 :对比 sync.Mutex、sync.RWMutex、sync/atomic 以及 sync.Map 的性能差异,选择最佳实现方式。
  2. 数据结构设计:定义计数器结构体,封装计数值和同步机制。
  3. 并发安全实现:采用选定的同步方案,完成增、减、获取操作。
  4. 压力测试:编写并发测试代码,验证线程安全性和性能。
  5. API接口封装:提供简单的对外接口,便于调用。
  6. 持久化实现(可选) :提供数据持久化接口(如JSON文件或SQLite数据库)。
  7. 统计接口实现:记录并发请求总数和最大并发量。
  8. 文档编写与代码整理:撰写代码使用文档及示例。

代码编写步骤

1. 数据结构设计

首先,定义一个计数器结构体,使用 sync/atomic 保证并发安全。

go 复制代码
go
複製編輯
package counter

import (
	"sync/atomic"
)

type Counter struct {
	value int64
}

func NewCounter() *Counter {
	return &Counter{}
}

2. 基本功能实现

提供递增、递减和获取当前值的方法。

scss 复制代码
go
複製編輯
// Increment 增加计数器值
func (c *Counter) Increment() {
	atomic.AddInt64(&c.value, 1)
}

// Decrement 减少计数器值
func (c *Counter) Decrement() {
	atomic.AddInt64(&c.value, -1)
}

// Get 获取当前计数器值
func (c *Counter) Get() int64 {
	return atomic.LoadInt64(&c.value)
}

3. 重置功能

增加一个重置方法。

scss 复制代码
go
複製編輯
// Reset 重置计数器
func (c *Counter) Reset() {
	atomic.StoreInt64(&c.value, 0)
}

4. 并发测试代码

编写测试代码,验证线程安全性。

go 复制代码
go
複製編輯
package main

import (
	"fmt"
	"sync"
	"counter"
)

func main() {
	c := counter.NewCounter()
	var wg sync.WaitGroup

	// 并发1000个协程进行递增
	for i := 0; i < 1000; i++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			c.Increment()
		}()
	}

	wg.Wait()
	fmt.Println("最终计数值:", c.Get())
}

5. 持久化(可选)

可以将计数器值写入文件。

go 复制代码
go
複製編輯
import (
	"os"
	"fmt"
)

func (c *Counter) SaveToFile(filename string) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	_, err = file.WriteString(fmt.Sprintf("%d", c.Get()))
	return err
}

6. 统计接口(可选)

增加统计并发访问量。

go 复制代码
go
複製編輯
type CounterWithStats struct {
	Counter
	totalAccess int64
}

func (cs *CounterWithStats) Increment() {
	atomic.AddInt64(&cs.totalAccess, 1)
	cs.Counter.Increment()
}

func (cs *CounterWithStats) GetTotalAccess() int64 {
	return atomic.LoadInt64(&cs.totalAccess)
}

性能测试结果

可以使用 go test -bench 进行基准测试。

示例基准代码:

go 复制代码
go
複製編輯
package counter

import "testing"

func BenchmarkIncrement(b *testing.B) {
	c := NewCounter()
	for i := 0; i < b.N; i++ {
		c.Increment()
	}
}

总结

  1. 代码线程安全 ✅
  2. 支持高并发 ✅
  3. 性能较优(sync/atomic 比 sync.Mutex 更快) ✅
  4. 可扩展功能:统计、持久化 ✅

收获

  1. atomic和sync.Mutex区别,atomic适合简单的原子计算,Mutex锁适合复杂的临界区操作
  2. go的结构体类继承的原理,和其他语言不一样,go的类继承其实是把其他结构体组合成了新的结构体
  3. benchmark测试的基础使用方法
相关推荐
小杨互联网10 小时前
Go 现代 CLI 工具离线镜像合集
开发语言·后端·golang·开源·go·命令行
hourinX12 小时前
从直接填格子到筛选图案:头像生成这部分是怎样改过来的—— gdenticon 重构日志(三)
go
我不会起名字32214 小时前
Redis 从 0 到 1(十三):分布式锁被误删、锁不住?Go 无 Lua 实现 3 级防御
redis·go·分布式锁·watch·setnx
hourinX1 天前
从 v1 到 v2,我重新拆了头像生成的整条链路—— gdenticon 重构日志(二)
go
FungLeo2 天前
成为全栈·Go 后端篇·分类树与标签关系:目录变更为何需要协调
golang·go·gorm·数据库事务·分类树·成为全栈
FungLeo2 天前
成为全栈·Go 后端篇·文章查询与展示投影:安全分页、排序和软删除
golang·go·gorm·分页查询·成为全栈·sql安全
KevinAshen672 天前
用 Go 修复旧下载工具:从接口 403 到凭证截断的完整复盘
go·github
FungLeo3 天前
成为全栈·Go 后端篇·跨语言认证兼容:Node 与 Go 如何互认 JWT 和密码
golang·go·jwt·身份认证·成为全栈·bcrypt
FungLeo4 天前
成为全栈·Go 后端篇·重写之前先锁基线:契约、Node 版本与行为清单
golang·node.js·go·openapi·契约测试·成为全栈
小满zs4 天前
Go语言第十七章(反射)
后端·go