如何获得延迟函数的引用?

时间:2013-08-26 23:06:54

标签: go

This article表示:“defer语句将函数调用推送到列表中。”我想知道我是否可以从程序中的其他位置访问该列表中的元素然后调用它们?我可以多次调用它们吗?我假设我有一个延迟行为的函数的引用(如果这有帮助)。

所以,这是我想要做的一个简短例子:

func main {
    doStuff = func() {
        // open database connections
        // write temporary files
        // etc...

        defer func() {
            // close database connections
            // delete temporary files
            // etc...
        }()
    }

    AwesomeApplication(doStuff)
}

func AwesomeApplication(doStuff func()) {
    // Now, can I get a reference to the defer function within `doStuff`?
    // No, I can't just define the defer function somewhere an pass it
    // with `doStuff`.  Think of this as a curiosity I want to satisfy,
    // not a real use case.
}

1 个答案:

答案 0 :(得分:11)

存储defer次调用的“列表”完全针对具体实施,因此您无法获得此列表的可靠方式 1,{ {3}} * g编译器系列的实现细节(虽然有点旧)可以找到2

延迟函数与当前goroutine(in Russ Cox' research blog)和 (在* g系列的情况下)由当前堆栈指针标识。如果当前堆栈帧匹配 存储在最顶层Defer条目中的堆栈帧,此函数被调用。

凭借这些知识, 可以使用cgo访问延迟函数列表。 你需要知道

  • 当前堆栈指针
  • 功能的地址
  • 当前的goroutine

但是,我不建议使用它。 您正在描述的用例的一般解决方案是使用这样的函数:

func setupRoutines() (setUp, tearDown func()) {
    // store db connection object and such

    return func() { /* connect db and such */ }, func() { /* close db and such */ }
}

然后,您可以在代码中共享tearDown函数,该函数将使用defer进行调用。 通过这种方式,你仍然拥有所有数据库连接和本地的连接,但你是 能够共享初始化/断开连接功能。

小提琴玩

如果您真的对使用unsafe和C感兴趣,可以使用以下代码作为模板。

检查/ runtime.c:

// +build gc
#include <runtime.h>

void ·FirstDeferred(void* foo) {
    foo = g->defer->fn;

    FLUSH(&foo);
}

检查/ inspect.go

package inspect

import "unsafe"

func FirstDeferred() unsafe.Pointer

defer.go

package main

import "defer/inspect"

func f(a, b int) {
    println("deferred f(", a, b, ")")
}

func main() {
    defer f(1, 2)
    println( inspect.FirstDeferred() )
}

此代码(基于g->Defer)使您可以访问当前的例行程序(g),因此 它的defer属性。因此,您应该能够访问该函数的指针 并将其包裹在一个FuncVal中并将其返回。