当前位置: 首页 > news >正文

sync.Once实现懒加载

懒加载概念

懒加载(Lazy Loading)是一种延迟初始化技术,核心思想是在真正需要时才创建或初始化对象,而不是在程序启动时就完成所有初始化工作。
核心特征:

  • 延迟初始化:对象在第一次使用时才被创建
  • 资源优化:避免不必要的资源消耗
  • 启动加速:减少应用程序启动时间
// 传统初始化 - 启动时立即执行
var db *Database = initDatabase()// 懒加载 - 使用时才初始化
var db *Database
var once sync.Oncefunc GetDB() *Database {once.Do(func() {db = initDatabase()})return db
}

适用场景

高成本资源初始化

  • 数据库连接池建立
  • 网络连接建立
  • 大型配置文件解析
  • 外部服务客户端初始化

sync.Once 原理

sync.Once 是 Go 语言标准库中提供的一个确保某个操作只执行一次的并发原语。其核心设计目标是在并发环境下保证初始化函数有且仅有一次成功执行,同时保持高性能。
数据结构

type Once struct {done uint32  // 原子标志位m    Mutex   // 互斥锁
}

实现模式详解

带错误处理的懒加载

type Database struct {conn   *sql.DBonce   sync.Onceerr    error
}func (db *Database) init() {db.conn, db.err = sql.Open("mysql", "user:pass@/dbname")if db.err == nil {db.err = db.conn.Ping()}
}func (db *Database) GetConnection() (*sql.DB, error) {db.once.Do(db.init)return db.conn, db.err
}

配置驱动的懒加载
初始化参数(配置)在运行时动态传入,懒加载过程依赖外部传入的配置信息。
特点:

  • 配置参数作为方法参数传递
  • 同一实例可接受不同配置(但只生效第一次)
  • 更灵活,支持运行时配置
type ConfigClient struct {config *Configclient *Clientonce   sync.Onceerr    error
}func (c *ConfigClient) lazyInit(config *Config) {c.config = configc.client, c.err = NewClient(config.Endpoint, config.Timeout)
}func (c *ConfigClient) GetClient(config *Config) (*Client, error) {c.once.Do(func() { c.lazyInit(config) })return c.client, c.err
}

适用场景:

  • 依赖外部配置的初始化
  • 动态参数传递
  • 多环境配置支持

连接驱动的懒加载
配置在实例创建时固定,懒加载过程使用预定义的配置建立连接。
特点:

  • 配置在构造时确定
  • 初始化过程完全内部化
  • 更稳定,配置不可变

问题

func newClientFromConfig() MongoRepository {cfg, err := loadConfigFromEnv()  // 在创建时加载配置if err != nil {return &mongoClient{err: err}}return &mongoClient{config: cfg}  // 配置在构造时固定
}func (c *mongoClient) lazyInit() error {c.once.Do(func() {// 使用预先设置的 c.config,不接收外部参数clientOptions := options.Client().ApplyURI(c.config.URI)// ... 使用固定配置建立连接})
}

全局单例懒加载

var (globalClient *ClientglobalOnce   sync.OnceglobalErr    error
)func GetGlobalClient() (*Client, error) {globalOnce.Do(func() {globalClient, globalErr = NewClient()})return globalClient, globalErr
}

实际使用

MongoDB 客户端实现

package mongoimport ("context""sync""time""go.mongodb.org/mongo-driver/mongo""go.mongodb.org/mongo-driver/mongo/options"
)type Client struct {config   *Configclient   *mongo.Clientdatabase *mongo.Databaseonce     sync.Onceerr      error
}func NewLazyClient(config *Config) *Client {return &Client{config: config}
}func (c *Client) lazyInit() {ctx, cancel := context.WithTimeout(context.Background(), c.config.Timeout)defer cancel()client, err := mongo.Connect(ctx, options.Client().ApplyURI(c.config.URI))if err != nil {c.err = errreturn}// 验证连接if err = client.Ping(ctx, nil); err != nil {client.Disconnect(context.Background())c.err = errreturn}c.client = clientc.database = client.Database(c.config.Database)
}func (c *Client) GetCollection(name string) (*mongo.Collection, error) {c.once.Do(c.lazyInit)if c.err != nil {return nil, c.err}return c.database.Collection(name), nil
}// 使用示例
func main() {client := NewLazyClient(&Config{URI:      "mongodb://localhost:27017",Database: "test",Timeout:  10 * time.Second,})// 第一次调用时才会真正建立连接collection, err := client.GetCollection("users")if err != nil {panic(err)}// 使用 collection...
}

Redis 客户端实现

package redisimport ("context""sync""time""github.com/redis/go-redis/v9"
)type LazyClient struct {config *RedisConfigclient redis.UniversalClientonce   sync.Onceerr    error
}func (lc *LazyClient) initClient() {if len(lc.config.Addrs) == 1 {lc.client = redis.NewClient(&redis.Options{Addr:     lc.config.Addrs[0],Password: lc.config.Password,DB:       lc.config.DB,})} else {lc.client = redis.NewClusterClient(&redis.ClusterOptions{Addrs:    lc.config.Addrs,Password: lc.config.Password,})}// 测试连接ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()if err := lc.client.Ping(ctx).Err(); err != nil {lc.client.Close()lc.err = err}
}func (lc *LazyClient) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {lc.once.Do(lc.initClient)if lc.err != nil {return lc.err}return lc.client.Set(ctx, key, value, expiration).Err()
}func (lc *LazyClient) Get(ctx context.Context, key string) (string, error) {lc.once.Do(lc.initClient)if lc.err != nil {return "", lc.err}return lc.client.Get(ctx, key).Result()
}

sync.Once 在提供线程安全的同时,保持了优异的性能表现,是 Go 语言中实现懒加载的首选方案。

http://www.dtcms.com/a/428718.html

相关文章:

  • 十三、格式化 HDFS 文件系统、启动伪分布式集群
  • 国产化+国际化双驱适配,ZStack Cloud v5.4.0 LTS应用市场一键解锁更丰富的云服务能力
  • 电商网站建站开发语言网络营销策划的主要特点
  • C++---运行时类型信息(Run-Time Type Information,RTTI)
  • php是前端还是后端大连谷歌seo
  • 学校网站 建设措施外贸网站优化公司
  • 基础建设期刊在哪个网站可以查万网cname解析
  • 深圳市建设工程网站十大装修公司排行榜
  • 【完整源码+数据集+部署教程】苹果病害图像分割系统: yolov8-seg-EfficientRepBiPAN
  • 网站搜索框如何做wordpress 文章php
  • 做网站算 自由职业者wordpress 自动连接
  • 安徽省建设厅官方网站毕业设计网站开发题目
  • 极速在线网站网站做记录访客
  • 中医理论、学派
  • 网站开发有哪几种语言html5制作网页的步骤
  • Spring AI alibaba RAG知识库基础
  • 9. Spring AI 当中对应 MCP 的操作
  • 网站优化网络重庆网页设计培训学校
  • 便携设备的技术革新:在方寸之间创造无限可能
  • C++---悬垂引用(Dangling Reference)
  • 公司网站制作设计价格内部网站如何建设
  • 深圳手机网站公司深圳英文网站建设去哪家
  • 有关建筑网站建设方案怎么做宣传网站
  • AI与云协作加持:深度解析Chaos Vantage 3.0 颠覆性更新
  • 机器学习算法以及code实现
  • 怎样开自己的网站个人域名可以做网站吗
  • 如何理解“物以类聚,人以群分”?
  • 南通企业网站怎么建设wordpress不用邮箱
  • 企业网站做的好宁波网站制作建设
  • 广州商城网站建设地址wordpress首页不显示整篇文章