var (
mu sync.Mutex
// std 定义了默认的全局 Logger.
std = NewLogger(NewOptions())
)
// Init 使用指定的选项初始化 Logger.
func Init(opts *Options) {
mu.Lock()
defer mu.Unlock()
std = NewLogger(opts)
}
一般一个项目日志也就 Init 一次吧,这里加这个锁是为了什么呢 🤔 求大佬解惑
完整源码: https://github.com/marmotedu/miniblog/blob/master/internal/pkg/log/log.go
1
bv 224 天前
这代码没什么营养,不要纠结为何这么写。
|
2
yuancoder 224 天前
这只是一个普通的函数
|
3
bv 224 天前 1
@bv 接 #1
1. 这个函数是 Init ,不是 init 2. 锁只锁了 std = NewLogger(opts) 语句,使用时没加锁,std 变量存在 race 问题。 3. func NewLogger(opts *Options) *zapLogger 可导出方法返回了不可导出的类型,不够优雅。 初学者还是应该拜读更规范的源码,免得入门时被低质量代码熏陶,一些错误理念会根深蒂固。 |
4
kuanat 224 天前 1
抱着解答问题的心态点进来,看见楼上的评论就笑了。名著你可以揣测一下作者的写作思路,地摊文学就算了……
代码层面的问题 @bv 在楼上已经说得比较清楚了,我补充一点我的理解。 这种代码水平在我之前的面试标准里面,按照初级/中级/高级三档水平,只能划分到初级那一档。我只通过看链接里那个文件就做出了判断,而且对此我非常有把握。 划分到初级的主要理由是:无法准确使用接口 Interface 来实现功能解耦。这个能力在我之前负责面试的时候是中级技能里的。相比之下,代码层面反倒是小问题了。 如果看不懂的话,我可以抽时间单独就 Go 通过接口来解耦专门做个解释。有隔壁那个代码氛围的帖子在,其实不想过多评价。但我还是要说,这段代码的作者的 Go 水平真就是初学者,出来卖课就是误人子弟了。 |
6
kele1997 224 天前
@aababc bv 说的第三点主要还是说返回了不可导出的类型吧,这种类型的变量在 go 里面感觉确实有点奇葩,其他包可以用,但是不能传递给其他函数(因为无法声明类型)。
另外复杂的结构体初始化可以用 Builder 或者 Option 模式吧 |
7
tcpdump 224 天前 via Android
啥教程?
|
8
CRVV 224 天前 via Android
加锁是对的,因为写了变量 std ,同时有两个线程写同一个变量是 data race 。
通常只会 init 一次,也不排除有人在多个线程上 init 这种情况。虽然这种用法大概率是错的,不过作为 library 让这种情况不出 data race 也是正常的。 使用的时候不需要加锁,因为同时有多个线程读同一个变量不是 data race |
9
kneo 224 天前 1
正确与否看具体的业务逻辑。一般来说这段代码如果真被并发调用,哪怕加了锁还是会初始化两次,不是典型的做法。如果希望初始化一次尽量使用 sync.Once 。
|
10
cabing 224 天前
为啥不用 once...
看了错误的代码示范了吧。。 |
12
changz 224 天前
我猜估计是 go race 的时候看到问题了,又懒得改
|
13
tbxark 224 天前
@bv 确实他应该下面这么写,我猜他这个 std 应该是想保留具体类型而不是接口类型
```go var _ Logger = &zapLogger{} var ( mu sync.Mutex std = newLogger(NewOptions()) ) func Init(opts *Options) { mu.Lock() defer mu.Unlock() std = newLogger(opts) } func NewLogger(opts *Options) Logger { return newLogger(opts) } func newLogger(opts *Options) *zapLogger { ``` |
14
lveye 223 天前
优雅一点的方式是使用 sync.Once (内部机制也是使用了锁),比如这样:
```var ( stdOnce sync.Once // std 定义了默认的全局 Logger. std = NewLogger(NewOptions()) ) // Init 使用指定的选项初始化 Logger. func Init(opts *Options) { stdOnce.Do(func() { std = NewLogger(opts) }) } ``` |