【问题标题】:Is there idiomatic scoped semantics in golang?golang 中是否有惯用的范围语义?
【发布时间】:2015-03-26 01:16:41
【问题描述】:

我想知道是否有任何惯用的方式来表示 scoped 语义。我所说的范围是指:

  • 作用域互斥体(oneliner 而不是显式锁定 + 延迟解锁),
  • 记录函数(或任何代码块)的入口和出口,
  • 测量执行时间。

前两个项目符号的示例代码:

package main

import "log"
import "sync"

func Scoped(m *sync.Mutex) func() {
    m.Lock()
    return func() {
        m.Unlock()
    }
}

func Log(what string) func() {
    log.Println(what, "started")
    return func() {
        log.Println(what, "done")
    }
}

func main() {
    defer Log("testing")()

    m := &sync.Mutex{} // obviously mutex should be from other source in real life
    defer Scoped(m)()
    // use m
}

https://play.golang.org/p/33j-GrBWSq

基本上我们需要进行一次函数调用刚刚(例如互斥锁),而一次调用应该推迟到延迟(例如互斥解锁)。我建议在这里只返回未命名的函数,但它可以很容易地命名(返回带有函数字段的结构)。

只有一个问题:用户可能会忘记“调用”第一次调用的结果。

这段代码是(可以)惯用的吗?

【问题讨论】:

  • 我想不出更短的方法。 “惯用”方式是显式调用lock(),然后调用defer unlock()
  • 这种方式在Effective Go trace example中使用。

标签: go deferred idioms scoped-lock


【解决方案1】:

以匿名函数为作用域

func() {
    Entrance()
    defer Exit()
    // anything you want to do in this scope
}()

【讨论】:

  • 不敢相信这不是最佳答案。
  • 它只是将接受答案的内容放在匿名函数中,所以这无论如何都不新颖
【解决方案2】:

您提出的解决方案已经很好了。您返回一个func 类型的值,您还必须在defer 的末尾调用它。

您可以避免这种情况(返回 func 值),但必须有 2 个函数调用,一个记录 start 事件,另一个记录 end em> 事件。

另一种方法是进行函数调用,该函数调用产生被延迟的函数的参数值(而不是返回函数),该函数使用defer 语句进行评估,这样它仍然可以保留一行。

您也可以在Go Playground上试用:

func start(s string) string {
    fmt.Println("Started", s)
    return s
}

func end(name string) {
    fmt.Println("Ended", name)
}

func main() {
    defer end(start("main"))

    fmt.Println("Doing main's work...")
}

输出:

Started main
Doing main's work...
Ended main

【讨论】:

  • 五年后看,看起来比公认的答案略好,主要是因为它使用了更简单的概念。我只会将start()/end() 的返回类型更改为某种自定义类型,以强制用户不会忘记调用start()(它仍然可以省略end(),但它可能不太可能。跨度>
【解决方案3】:

我不相信有一种惯用的方法可以做到这一点。我不知道你为什么要写,写起来真的那么糟糕吗

m.Lock()
defer m.Unlock()

?

【讨论】:

  • 这确实是惯用的方式(天哪,我开始讨厌所有这些惯用的废话),但是 OP 提出的模式在 IMO 中相当不错。
  • 'Logging engine' 是最好的,当您必须只输入一行时 - 越多,用户放弃登录某些小功能的可能性就越大。
  • 也有可能使用错误变量(i 而不是 j)或在我们的示例记录器中键入不同的字符串(这可能会导致混淆日志分析工具)。
【解决方案4】:

我认为问题与 Go 的惯用性无关,似乎当函数的行为与任一调用相同时,对代码进行推理通常会更好。为了保持状态,我最好创建一个对象并将函数定义为该对象的方法。意思是像

type message string
func (foo message) Log(bar string){
    if bar==nil{doSomethingSpecial()}
    switch foo{
        case something: doSomething()
        ...
        case nil: doSomethingInitial()
        default: doDefault()
    }   
    log.Println(bar, "started")
    foo=bar
} 

【讨论】:

    猜你喜欢
    • 2016-11-06
    • 1970-01-01
    • 2016-11-20
    • 2019-05-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多