返回顶部
首页 > 资讯 > 后端开发 > GO >go sync.Once实现高效单例模式详解
  • 783
分享到

go sync.Once实现高效单例模式详解

go sync.Once单例模式go sync.Once 2023-03-14 11:03:31 783人浏览 泡泡鱼
摘要

目录1. 简介2. 基本实现2.1 单例模式定义2.2 sync.Once实现单例模式2.3 其他方式实现单例模式2.3.1 全局变量定义时赋值,实现单例模式2.3.2 init 函

1. 简介

本文介绍使用sync.Once来实现单例模式,包括单例模式的定义,以及使用sync.Once实现单例模式的示例,同时也比较了其他单例模式的实现。最后以一个开源框架中使用sync.Once实现单例模式的例子来作为结尾。

2. 基本实现

2.1 单例模式定义

单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点来访问这个实例。在整个应用程序中,所有对于这个类的访问都将返回同一个实例对象。

2.2 sync.Once实现单例模式

下面是一个简单的示例代码,使用 sync.Once 实现单例模式:

 package singleton
 import "sync"
 type singleton struct {
     // 单例对象的状态
 }
 var (
     instance *singleton
     once     sync.Once
 )
 func GetInstance() *singleton {
     once.Do(func() {
         instance = &singleton{}
         // 初始化单例对象的状态
     })
     return instance
 }

在上面的示例代码中,我们定义了一个 singleton 结构体表示单例对象的状态,然后将它的实例作为一个包级别的变量 instance,并使用一个 once 变量来保证 GetInstance 函数只被执行一次。

GetInstance 函数中,我们使用 once.Do 方法来执行一个初始化单例对象。由于 once.Do 方法是基于原子操作实现的,因此可以保证并发安全,即使有多个协程同时调用 GetInstance 函数,最终也只会创建一个对象。

2.3 其他方式实现单例模式

2.3.1 全局变量定义时赋值,实现单例模式

Go 语言中,全局变量会在程序启动时自动初始化。因此,如果在定义全局变量时给它赋值,则对象的创建也会在程序启动时完成,可以通过此来实现单例模式,以下是一个示例代码:

type MySingleton struct {
    // 字段定义
}
var mySingletonInstance = &MySingleton{
    // 初始化字段
}
func GetMySingletonInstance() *MySingleton {
    return mySingletonInstance
}

在上面的代码中,我们定义了一个全局变量 mySingletonInstance 并在定义时进行了赋值,从而在程序启动时完成了对象的创建和初始化。在 GetMySingletonInstance 函数中,我们可以直接返回全局变量 mySingletonInstance,从而实现单例模式。

2.3.2 init 函数实现单例模式

在 Go 语言中,我们可以使用 init 函数来实现单例模式。init 函数是在包被加载时自动执行的函数,因此我们可以在其中创建并初始化单例对象,从而保证在程序启动时就完成对象的创建。以下是一个示例代码:

package main
type MySingleton struct {
    // 字段定义
}
var mySingletonInstance *MySingleton
func init() {
    mySingletonInstance = &MySingleton{
        // 初始化字段
    }
}
func GetMySingletonInstance() *MySingleton {
    return mySingletonInstance
}

在上面的代码中,我们定义了一个包级别的全局变量 mySingletonInstance,并在 init 函数中创建并初始化了该对象。在 GetMySingletonInstance 函数中,我们直接返回该全局变量,从而实现单例模式。

2.3.3 使用互斥锁实现单例模式

在 Go 语言中,可以只使用一个互斥锁来实现单例模式。下面是一个简单代码的演示:

var instance *MySingleton
var mu sync.Mutex
func GetMySingletonInstance() *MySingleton {
   mu.Lock()
   defer mu.Unlock()
   if instance == nil {
      instance = &MySingleton{
         // 初始化字段
      }
   }
   return instance
}

在上面的代码中,我们使用了一个全局变量instance来存储单例对象,并使用了一个互斥锁 mu 来保证对象的创建和初始化。具体地,我们在 GetMySingletonInstance 函数中首先加锁,然后判断 instance 是否已经被创建,如果未被创建,则创建并初始化对象。最后,我们释放锁并返回单例对象。

需要注意的是,在并发高的情况下,使用一个互斥锁来实现单例模式可能会导致性能问题。因为在一个 goroutine 获得锁并创建对象时,其他的 goroutine 都需要等待,这可能会导致程序变慢。

2.4 使用sync.Once实现单例模式的优点

相对于init 方法和使用全局变量定义赋值单例模式的实现,sync.Once 实现单例模式可以实现延迟初始化,即在第一次使用单例对象时才进行创建和初始化。这可以避免在程序启动时就进行对象的创建和初始化,以及可能造成的资源的浪费。

而相对于使用互斥锁实现单例模式,使用 sync.Once 实现单例模式的优点在于更为简单和高效。sync.Once提供了一个简单的接口,只需要传递一个初始化函数即可。相比互斥锁实现方式需要手动处理锁、判断等操作,使用起来更加方便。而且使用互斥锁实现单例模式需要在每次访问单例对象时进行加锁和解锁操作,这会增加额外的开销。而使用 sync.Once 实现单例模式则可以避免这些开销,只需要在第一次访问单例对象时进行一次初始化操作即可。

但是也不是说sync.Once便适合所有的场景,这个是需要具体情况具体分析的。下面说明sync.Onceinit方法,在哪些场景下使用init更好,在哪些场景下使用sync.Once更好。

2.5 sync.Once和init方法适用场景

对于init实现单例,比较适用于在程序启动时就需要初始化变量的场景。因为init函数是在程序运行前执行的,可以确保变量在程序运行时已经被初始化。

对于需要延迟初始化某些对象,对象被创建出来并不会被马上使用,或者可能用不到,例如创建数据库连接池等。这时候使用sync.Once就非常合适。它可以保证对象只被初始化一次,并且在需要使用时才会被创建,避免不必要的资源浪费。

3. gin中单例模式的使用

3.1 背景

这里首先需要介绍下gin.Engine, gin.Engine是Gin框架的核心组件,负责处理Http请求,路由请求到对应的处理器,处理器可以是中间件、控制器或处理HTTP响应等。每个gin.Engine实例都拥有自己的路由表、中间件栈和其他配置项,通过调用其方法可以注册路由、中间件、处理函数等。

一个HTTP服务器,只会存在一个对应的gin.Engine实例,其保存了路由映射规则等内容。

为了简化开发者Gin框架的使用,不需要用户创建gin.Engine实例,便能够完成路由的注册等操作,提高代码的可读性和可维护性,避免重复代码的出现。这里对于一些常用的功能,抽取出一些函数来使用,函数签名如下:

// ginS/gins.go
// 加载html模版文件
func LoadHTMLGlob(pattern string) {}
// 注册POST请求处理器
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {}
// 注册GET请求处理器
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {}
// 启动一个HTTP服务器
func Run(addr ...string) (err error) {}
// 等等...

接下来需要对这些函数来进行实现。

3.2 具体实现

首先从使用出发,这里使用POST方法/GET方法注册请求处理器,然后使用Run方法启动服务器:

func main() {
   // 注册url对应的处理器
   POST("/login", func(c *gin.Context) {})
   // 注册url对应的处理器
   GET("/hello", func(c *gin.Context) {})
   // 启动服务
   Run(":8080")
}

这里我们想要的效果,应该是调用Run方法启动服务后,往/login路径发送请求,此时应该执行我们注册的对应处理器,往/hello路径发送请求也是同理。

所以,这里POST方法,GET方法,Run方法应该都是对同一个gin.Engine 进行操作的,而不是各自使用各自的gin.Engine实例,亦或者每次调用就创建一个gin.Engine实例。这样子才能达到我们预想的效果。

所以,我们需要实现一个方法,获取gin.Engine实例,每次调用该方法都是获取到同一个实例,这个其实也就是单例的定义。然后POST方法,GET方法又或者是Run方法,调用该方法获取到gin.Engine实例,然后调用实例去调用对应的方法,完成url处理器的注册或者是服务的启动。这样子就能够保证是使用同一个gin.Engine实例了。具体实现如下:

// ginS/gins.go
import (
   "GitHub.com/gin-gonic/gin"
)
var once sync.Once
var internalEngine *gin.Engine
func engine() *gin.Engine {
   once.Do(func() {
      internalEngine = gin.Default()
   })
   return internalEngine
}
// POST is a shortcut for router.Handle("POST", path, handle)
func POST(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
   return engine().POST(relativePath, handlers...)
}
// GET is a shortcut for router.Handle("GET", path, handle)
func GET(relativePath string, handlers ...gin.HandlerFunc) gin.IRoutes {
   return engine().GET(relativePath, handlers...)
}

这里engine() 方法使用了 sync.Once 实现单例模式,确保每次调用该方法返回的都是同一个 gin.Engine 实例。然后POST/GET/Run方法通过该方法获取到gin.Engine实例,然后调用实例中对应的方法来完成对应的功能,从而达到POST/GET/Run等方法都是使用同一个实例操作的效果。

3.3 sync.Once实现单例的好处

这里想要达到的目的,其实是GET/POST/Run等抽取出来的函数,使用同一个gin.Engine实例。

为了达到这个目的,我们其实可以在定义internalEngine 变量时,便对其进行赋值;或者是通init函数完成对internalEngine变量的赋值,其实都可以。

但是我们抽取出来的函数,用户并不一定使用,定义时便初始化或者在init方法中便完成了对变量的赋值,用户没使用的话,创建出来的gin.Engine实例没有实际用途,造成了不必要的资源的浪费。

而engine方法使用sync.Once实现了internalEngin的延迟初始化,只有在真正使用到internalEngine时,才会对其进行初始化,避免了不必要的资源的浪费。

这里其实也印证了上面我们所说的sync.Once的适用场景,对于不会马上使用的单例对象,此时可以使用sync.Once来实现。

4.总结

单例模式是一种常用的设计模式,用于保证一个类仅有一个实例。在单例模式中,常常使用互斥锁或者变量赋值的方式来实现单例。然而,使用sync.Once可以更方便地实现单例,同时也能够避免了不必要的资源浪费。当然,没有任何一种实现是适合所有场景的,我们需要根据具体场景具体分析。

以上就是go sync.Once实现高效单例模式详解的详细内容,更多关于sync.Once单例模式的资料请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: go sync.Once实现高效单例模式详解

本文链接: https://lsjlt.com/news/199665.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • go sync.Once实现高效单例模式详解
    目录1. 简介2. 基本实现2.1 单例模式定义2.2 sync.Once实现单例模式2.3 其他方式实现单例模式2.3.1 全局变量定义时赋值,实现单例模式2.3.2 init 函...
    99+
    2023-03-14
    go sync.Once单例模式 go sync.Once
  • go sync.Once如何实现高效单例模式
    这篇文章主要讲解了“go sync.Once如何实现高效单例模式”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“go sync.Once如何实现高效单例模式”吧!基本实现1...
    99+
    2023-07-05
  • Golang sync.Once实现单例模式的方法详解
    目录1. sync.Once 的原理和实现2. sync.Once 的错误处理3. sync.Once 的嵌套调用4. 并发性能5. 总结Go 语言的 sync 包提供了一系列同步原...
    99+
    2023-05-18
    Golang sync.Once实现单例模式 Golang sync.Once原理 Golang sync.Once使用 Golang sync.Once
  • Go语言单例模式详解
    目录什么是单例模式Go中实现单例模式的方式方法一: 懒汉式方法二:饿汉式方法三:双重检查锁定示例总结单例模式是一种常见的设计模式,它在系统中仅允许创建一个实例来控制对某些资源的访问。...
    99+
    2023-03-23
    Go语言单例模式
  • Go语言单例模式示例详解
    目录简单单例模式加锁的单例模式双check 的单例模式sync.Once 的单例模式简单单例模式 单例模式是创建类型的模式,它是为了保证执行期间内只有一个实例。使用 Golang 指...
    99+
    2024-04-02
  • Python单例模式实例详解
    本文实例讲述了Python单例模式。分享给大家供大家参考,具体如下: 单例模式:保证一个类仅有一个实例,并提供一个访问他的全局访问点。 实现某个类只有一个实例的途径: 1,让一个全局变量使得一个对象被访问,...
    99+
    2022-06-04
    详解 实例 模式
  • 详解Go语言设计模式之单例模式
    目录单例模式的概念单例模式结构单例模式的使用场景单例模式例子:特殊的计数器第一个单元测试单例模式实现单例模式优缺点单例模式的概念 单例模式很容易记住。就像名称一样,它只能提供对象的单...
    99+
    2024-04-02
  • Go并发编程之sync.Once使用实例详解
    目录一.序二. 源码分析2.1结构体2.2 接口三. 使用场景案例3.1 单例模式3.2 加载配置文件示例四.总结五. 参考一.序 单从库名大概就能猜出其作用。sync.Once使用...
    99+
    2024-04-02
  • java设计模式-单例模式实现方法详解
    目录饿汉式静态变量静态代码块懒汉式线程不安全线程安全双重检查静态内部类总结单例模式,属于创建类型的一种常用的软件设计模式。通过单例模式的方法创建的类在当前进程中只有一个实例(根据需要...
    99+
    2024-04-02
  • java 单例模式的实例详解
    java 单例模式的实例详解概念:    java中单例模式是一种常见的设计模式,单例模式分三种:懒汉式单例、饿汉式单例、登记式单例三种。    单例模式有一下特点:   1、单例类只能有一个实例。   2、单例类必须自己自己创建自己的唯一...
    99+
    2023-05-31
    java 单例模式 ava
  • go 分布式锁简单实现实例详解
    目录正文案例资源加锁使用redis来实现分布式锁redis lua保证原子性正文 其实锁这种东西,都能能不加就不加,锁会导致程序一定程度上退回到串行化,进而降低效率。 案例 首先,看...
    99+
    2024-04-02
  • 单例模式详解
    目录 一、什么是单例模式 二、单例模式的结构 三、单例模式分类 四、单例模式优缺点 五、创建单例模式 饿汉式 1. 静态成员变量方式 2.静态代码块方式 懒汉式 1.线程不安全 2.线程安全(优化) 3.双重检查锁模式 4. 静态内部类方...
    99+
    2023-10-02
    设计模式 单例模式 java 面试
  • java 单例模式和工厂模式实例详解
    单例模式根据实例化对象时机的不同分为两种:一种是饿汉式单例,一种是懒汉式单例。私有的构造方法指向自己实例的私有静态引用以自己实例为返回值的静态的公有的方法饿汉式单例 public class Singleton { private ...
    99+
    2023-05-31
    java 单例模式 工厂模式
  • Java之单例模式实现方案详解
      单例模式是最常用到的设计模式之一,熟悉设计模式的朋友对单例模式都不会陌生。一般介绍单例模式的书籍都会提到 饿汉式 和 懒汉式 这两种实现方式。但是除了这两种方式,本文还会介绍其他...
    99+
    2024-04-02
  • kotlinobject关键字单例模式实现示例详解
    目录正文一、 匿名内部类二、单例模式三、伴生对象1、深入分析伴生对象2、用伴生对象实现工厂模式3、用伴生对象实现单例模式(1)、借助懒加载委托(2)、伴生对象 Double Chec...
    99+
    2023-01-12
    kotlin object关键字单例模式 kotlin object
  • Java单例模式的6种实现方式详解
    目录为什么使用单例模式使用单例模式需要注意的关键点单例模式的几种写法1. 饿汉式2. 懒汉式3. DCL(Double CheckLock)实现单例4. 静态内部类5...
    99+
    2024-04-02
  • Python实现单例模式的四种方式详解
    简介:单例模式可以保证一个类仅有一个实例,并提供一个访问它的全局访问点。适用性于当类只能有一个实例而且客户可以从一个众所周知的访问点访问它,例如访问数据库、MQ等。 实现方式: 1、...
    99+
    2024-04-02
  • Go单例模式与Once源码实现
    目录单例实现sync.Once源码分析1. lock并不会同步值2. Do执行一次3. Once执行Do后不准copy4. Do并发时阻塞5. Do递归死锁单例实现 type sin...
    99+
    2022-12-08
    go单例模式 go Once源码
  • Go语言单例模式怎么实现
    这篇文章主要介绍“Go语言单例模式怎么实现”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Go语言单例模式怎么实现”文章能帮助大家解决问题。什么是单例模式单例模式指仅允许创建一个对象的设计模式。它通常...
    99+
    2023-07-05
  • C++实现单例模式日志输出详解
    目录一、单例模式简单介绍1.1 基础介绍1.2 单例模式使用场景二、单例模式实现日志记录(Singleton Logger)三、总结一、单例模式简单介绍 1.1 基础介绍 单例模式只...
    99+
    2023-05-17
    C++单例模式 日志输出 C++单例模式 C++ 日志输出
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作