小编典典

如何获得对延迟功能的引用?

go

本文指出:“ 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.
}

阅读 178

收藏
2020-07-02

共1个答案

小编典典

defer调用存储的“列表” 完全是特定于实现的,因此您 没有可靠的方式访问此列表
12个为*
G编译器家族中的实现细节(虽然有点老),可以发现在拉斯考克斯的研究博客

延迟函数与当前goroutine(g->Defer)和当前堆栈指针标识的(对于*
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 一起玩,可以将以下代码用作模板。

检查/运行时.c:

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

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

    FLUSH(&foo);
}

inspect / 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() )
}

该代码(基于this)使您可以访问当前的go例程(g),从而可以访问其defer属性。因此,您应该能够访问该函数的指针并将其包装在go中FuncVal并返回它。

2020-07-02