在看某个 golang 的教程,不太理解为啥 Init Logger 要加锁 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
dimtutac
V2EX    Go 编程语言

在看某个 golang 的教程,不太理解为啥 Init Logger 要加锁

  •  
  •   dimtutac 2024-05-07 16:13:09 +08:00 3106 次点击
    这是一个创建于 521 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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

    14 条回复    2024-05-08 21:15:58 +08:00
    bv
        1
    bv  
       2024-05-07 16:43:52 +08:00
    这代码没什么营养,不要纠结为何这么写。
    yuancoder
        2
    yuancoder  
       2024-05-07 16:47:44 +08:00
    这只是一个普通的函数
    bv
        3
    bv  
       2024-05-07 17:05:56 +08:00   1
    @bv 接 #1
    1. 这个函数是 Init ,不是 init
    2. 锁只锁了 std = NewLogger(opts) 语句,使用时没加锁,std 变量存在 race 问题。
    3. func NewLogger(opts *Options) *zapLogger 可导出方法返回了不可导出的类型,不够优雅。

    初学者还是应该拜读更规范的源码,免得入门时被低质量代码熏陶,一些错误理念会根深蒂固。
    kuanat
        4
    kuanat  
       2024-05-07 18:38:59 +08:00   1
    抱着解答问题的心态点进来,看见楼上的评论就笑了。名著你可以揣测一下作者的写作思路,地摊文学就算了……

    代码层面的问题 @bv 在楼上已经说得比较清楚了,我补充一点我的理解。

    这种代码水平在我之前的面试标准里面,按照初级/中级/高级三档水平,只能划分到初级那一档。我只通过看链接里那个文件就做出了判断,而且对此我非常有把握。

    划分到初级的主要理由是:无法准确使用接口 Interface 来实现功能解耦。这个能力在我之前负责面试的时候是中级技能里的。相比之下,代码层面反倒是小问题了。

    如果看不懂的话,我可以抽时间单独就 Go 通过接口来解耦专门做个解释。有隔壁那个代码氛围的帖子在,其实不想过多评价。但我还是要说,这段代码的作者的 Go 水平真就是初学者,出来卖课就是误人子弟了。
    aababc
        5
    aababc  
       2024-05-07 19:24:51 +08:00
    @bv 这里的第三点说说我的想法,大佬看看是否有问题,如果结构体需要比较复杂的初始化流程或者说在结构体初始化完成之后需要对其他的字段进行赋值,这么做应该也没有问题吧?
    kele1997
        6
    kele1997  
       2024-05-07 19:43:06 +08:00
    @aababc bv 说的第三点主要还是说返回了不可导出的类型吧,这种类型的变量在 go 里面感觉确实有点奇葩,其他包可以用,但是不能传递给其他函数(因为无法声明类型)。

    另外复杂的结构体初始化可以用 Builder 或者 Option 模式吧
    tcpdump
        7
    tcpdump  
       2024-05-07 20:08:25 +08:00 via Android
    啥教程?
    CRVV
        8
    CRVV  
       2024-05-07 21:36:49 +08:00 via Android
    加锁是对的,因为写了变量 std ,同时有两个线程写同一个变量是 data race 。
    通常只会 init 一次,也不排除有人在多个线程上 init 这种情况。虽然这种用法大概率是错的,不过作为 library 让这种情况不出 data race 也是正常的。

    使用的时候不需要加锁,因为同时有多个线程读同一个变量不是 data race
    kneo
        9
    kneo  
       2024-05-07 21:52:24 +08:00   1
    正确与否看具体的业务逻辑。一般来说这段代码如果真被并发调用,哪怕加了锁还是会初始化两次,不是典型的做法。如果希望初始化一次尽量使用 sync.Once 。
    cabing
        10
    cabing  
       2024-05-07 22:18:34 +08:00
    为啥不用 once...

    看了错误的代码示范了吧。。
    aababc
        11
    aababc  
       2024-05-08 10:05:10 +08:00
    @kele1997 确实,我一直想着依赖接口,忽略了直接使用类型的情况。
    changz
        12
    changz  
       2024-05-08 11:44:08 +08:00
    我猜估计是 go race 的时候看到问题了,又懒得改
    tbxark
        13
    tbxark  
       2024-05-08 13:53:52 +08:00
    @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 {
    ```
    lveye
        14
    lveye  
       2024-05-08 21:15:58 +08:00
    优雅一点的方式是使用 sync.Once (内部机制也是使用了锁),比如这样:
    ```var (
    stdOnce sync.Once

    // std 定义了默认的全局 Logger.
    std = NewLogger(NewOptions())
    )

    // Init 使用指定的选项初始化 Logger.
    func Init(opts *Options) {
    stdOnce.Do(func() {
    std = NewLogger(opts)
    })
    }
    ```
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3834 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 23ms UTC 05:11 PVG 13:11 LAX 22:11 JFK 01:11
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86