有没有一种安全的方法来保持使用CGo从C代码引用Go变量?

Jam*_*dge 5 garbage-collection go cgo

当使用CGo将C代码与Go连接时,如果我在C端保留对Go变量的引用,那么我是否会冒被垃圾收集器释放该对象的风险,或者GC会看到由所管理的变量中的指针C方面?

为了说明我的要求,请考虑以下示例程序:

去代码:

package main

/*
typedef struct _Foo Foo;
Foo *foo_new(void);
void foo_send(Foo *foo, int x);
int foo_recv(Foo *foo);
*/
import "C"

//export makeChannel
func makeChannel() chan int {
    return make(chan int, 1)
}

//export sendInt
func sendInt(ch chan int, x int) {
    ch <- x
}

//export recvInt
func recvInt(ch chan int) int {
    return <-ch
}

func main() {
    foo := C.foo_new()
    C.foo_send(foo, 42)
    println(C.foo_recv(foo))
}
Run Code Online (Sandbox Code Playgroud)

C代码:

#include <stdlib.h>
#include "_cgo_export.h"

struct _Foo {
    GoChan ch;
};

Foo *foo_new(void) {
    Foo *foo = malloc(sizeof(Foo));
    foo->ch = makeChannel();
    return foo;
}

void foo_send(Foo *foo, int x) {
    sendInt(foo->ch, x);
}

int foo_recv(Foo *foo) {
    return recvInt(foo->ch);
}
Run Code Online (Sandbox Code Playgroud)

怎样运行的风险foo->ch由之间的垃圾收集器被释放foo_newfoo_send电话?如果是这样,有没有办法从C端固定Go变量,以防止在我持有对它的引用时释放它?

Int*_*net 3

根据gmp CGo 示例

垃圾收集是个大问题。对于 Go 世界来说,拥有指向 C 世界的指针并在不再需要时释放这些指针是很好的。为了提供帮助,Go 代码可以定义持有 C 指针的 Go 对象,并对这些 Go 对象使用 runtime.SetFinalizer。

对于 C 世界来说,拥有指向 Go 世界的指针要困难得多,因为 Go 垃圾收集器不知道 C 分配的内存。最重要的考虑因素是不限制未来的实现,因此规则是 Go 代码可以将 Go 指针传递给 C 代码,但必须单独安排 Go 保留对指针的引用,直到 C 完成它。

所以我不确定你是否可以从C端固定变量,但你也许可以通过使用该runtime.SetFinalizer函数从Go端控制变量的垃圾收集。

希望有帮助。

  • 或者:实时 Go 数据必须始终从 Go 堆引用,因为这就是 GC 将扫描的全部内容。有时,为了实现这一点,您的 C 可以调用 Go 函数来分配引用保持类型。(毕竟,只要所有引用都在 Go 堆中,就可以了。)其他时候,引用来自 malloc 分配的内存,因此您必须编写 malloc/free 包装器或其他东西来人为创建对的引用来自 Go-land 中其他地方的 Go 对象。例如,这些包装器可能是在“map[uintptr]interface{}”中添加/删除条目的 Go 函数。 (2认同)
  • 谢谢。我已经决定在 Go 端管理通道的映射:当我想将它们存储在 C 端时将其添加到映射中,当我想在 C 端释放它们时将它们从映射中删除。 (2认同)