golang 中 init func 和 var 谁先执行问题

本文详细解析了Golang中变量初始化、init函数及main函数的执行顺序,揭示了Golang程序启动时的关键流程。了解这些基础知识对于深入理解Golang运行机制至关重要。

golang 中有的 init 函数会先 main 函数执行,var 会先 init 函数执行,所以执行顺序为 var --> init() --> main() 。

/**
 * @Author: v_bivwei
 * @Description:
 * @Date: 2020/7/4 16:44
 */
package main

import "fmt"

func init() {
	fmt.Println("init func")
}

func main() {
	fmt.Println("main func")
}

在这里插入图片描述

/**
 * @Author: v_bivwei
 * @Description:
 * @Date: 2020/7/4 16:50
 */
package main

import "fmt"

func init() {
	fmt.Println("init func")
	global = 0
}

func convert() int {
	fmt.Println("convert func")
	return 100
}

var global = convert()

func main() {
	fmt.Println("main func global = ", global)
}

在这里插入图片描述

<think>好的,我现在需要回答用户关于Golang中sync.Once.Do方法的使用场景实现原理的问题。首先,我得回忆一下sync.Once的基本概念。根据引用[2][3],sync.Once主要用于确保某个操作在并发环境下只执行一次,比如加载配置文件或者初始化单例。用户可能想知道什么时候应该使用它,以及它是如何做到线程安全的。接下来,关于使用场景,引用[1]提到它是处理“执行一次且仅执行一次”的首选工具,所以需要列举一些典型例子,比如配置加载、数据库连接池初始化、单例模式等。用户可能关心实际开发中哪些情况适合用Once,这部分需要结合引用中的例子来展开。然后是实现原理。根据引用[3],Once结构体内部有一个done标志互斥锁m。第一次调用Do方法时会通过原子操作检查done是否为0,如果是则获取锁执行函数,执行完成后原子存储done为1。后续调用直接跳过。需要注意的是,即使函数f发生panic,Once也会认为已经执行过了,这可能是一个陷阱,需要提醒用户。另外,引用[4]提到了如何实现可重试的初始化,可能需要自定义结构体包装Once,但原生的Once不支持重置,这点需要说明清楚。还有,引用[5]中的代码示例展示了在并发错误处理中使用Once来捕获第一个错误,这也是一个使用场景的例子。用户可能还关心Once的线程安全机制,为什么使用原子操作互斥锁结合。需要解释双重检查锁定的原理,先原子读取done,如果为0再加锁,避免多次加锁影响性能。同时,done字段在结构体中的位置优化,放在第一个字段是为了利用CPU缓存的优势,减少指令数量,这点引用[3]有提到。最后,注意事项部分,比如函数f不能有参数,需要闭包;不能重复调用;panic后的处理等,都是需要强调的。用户可能在实际使用中遇到这些问题,提前说明可以避免常见错误。总结回答的结构应该分为使用场景、实现原理、注意事项,并引用相关的资料来支持每个部分。需要确保语言清晰,例子具体,原理讲解透彻,同时避免技术术语过多导致理解困难。可能还需要生成相关问题,帮助用户进一步学习,比如与单例模式的关系、与init函数的区别、性能优化等。</think>### Golang sync.Once.Do 方法详解 #### 一、使用场景 `sync.Once.Do` 是 Go 语言中实现 **一次性初始化** 的核心工具,适用于以下场景: 1. **单例模式** 确保全局唯一实例的创建(如数据库连接池、日志管理器)[^3]。 2. **配置加载** 在多协程环境下保证配置文件只加载一次[^2][^4]。 3. **资源初始化** 如缓存预热、依赖注入容器初始化。 4. **并发错误处理** 在多个协程中仅捕获第一个错误(通过闭包配合 `Once` 实现)[^5]。 #### 二、实现原理 `sync.Once` 结构体包含两个关键字段: ```go type Once struct { done uint32 // 原子标志位 m Mutex // 互斥锁 } ``` 其核心逻辑通过 **双重检查锁定(Double-Checked Locking)** 实现: 1. **原子检查** 调用 `Do(f)` 时,先通过 `atomic.LoadUint32(&o.done)` 判断是否已执行。 2. **锁定执行** 若 `done == 0`,则加锁 `m.Lock()`,再次检查 `done` 状态,防止竞态条件。 3. **执行函数** 调用 `f()`,通过 `atomic.StoreUint32(&o.done, 1)` 标记完成[^3][^4]。 #### 三、关键特性与注意事项 1. **线程安全保证** 通过原子操作互斥锁结合,确保函数 `f` 仅执行一次[^3][^4]。 2. **Panic 处理** 若 `f` 发生 panic,`Once` 仍会标记 `done=1`,后续调用不再执行[^4]。 3. **参数传递限制** `Do` 方法的函数 `f` 必须通过闭包传递参数[^2]。 4. **不可重置性** 原生 `Once` 不支持重复初始化,需自定义结构体实现重试逻辑[^4]。 --- ### 代码示例 ```go var ( instance *Singleton once sync.Once ) func GetInstance() *Singleton { once.Do(func() { instance = &Singleton{data: "initialized"} }) return instance } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值