Golang中Cgo安全引用Go对象的"export-handle"问题

Golang中Cgo安全引用Go对象的"export-handle"问题 大家好,这是我的第一个关于Go和CGO的帖子及问题。

我需要在C语言中存储一个Go引用。目前我使用以下伪代码:

  1. 创建一个全局映射

    var lockService = make(map[MyUUID]MyInterface)
    

    (这不是线程安全的)

  2. 将MyUUID(其最大长度为C指针的长度,64位)保存在C中

    var uid MyUUID = (generate)
    C.MyUUIDset(uid)
    lockService[uid] = MyInterfaceObject
    
  3. 通过以下方式取回我的MyInterface:

    uid := C.MyUUIDget()
    lockService[uid].MyFunc()
    

问题:Go是否有“内置”的方法来解决“导出句柄”问题?


更多关于Golang中Cgo安全引用Go对象的"export-handle"问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

2 回复

我认为你的解决方案——自己生成句柄并将其传递给C而不是Go指针——已经相当不错了。

更多关于Golang中Cgo安全引用Go对象的"export-handle"问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中,Cgo确实没有内置的“export-handle”机制来安全地在C中存储Go对象引用。你的方法本质上是正确的,但需要改进线程安全性和内存管理。以下是更完整的实现示例:

package main

/*
#include <stdint.h>
#include <stdlib.h>

typedef uint64_t Handle;

extern Handle create_handle(void* go_obj);
extern void* get_go_object(Handle h);
extern void free_handle(Handle h);
*/
import "C"
import (
    "sync"
    "unsafe"
)

var (
    handles sync.Map // 线程安全的map替代方案
    counter uint64
    mu      sync.Mutex
)

// MyInterface 示例接口
type MyInterface interface {
    MyFunc()
}

//export create_handle
func create_handle(go_obj unsafe.Pointer) C.Handle {
    mu.Lock()
    counter++
    h := counter
    mu.Unlock()
    
    handles.Store(h, (*MyInterface)(go_obj))
    return C.Handle(h)
}

//export get_go_object
func get_go_object(h C.Handle) unsafe.Pointer {
    if val, ok := handles.Load(uint64(h)); ok {
        if obj, ok := val.(*MyInterface); ok {
            return unsafe.Pointer(obj)
        }
    }
    return nil
}

//export free_handle
func free_handle(h C.Handle) {
    handles.Delete(uint64(h))
}

// 示例C函数包装
func CreateHandleForC(obj MyInterface) C.Handle {
    return create_handle(unsafe.Pointer(&obj))
}

// 在C代码中使用:
/*
Handle h = create_handle(go_obj);
// ... 稍后使用 ...
MyInterface* obj = (MyInterface*)get_go_object(h);
if (obj) {
    // 调用Go方法
}
free_handle(h);
*/

更安全的替代方案是使用runtime/cgo.Handle(Go 1.17+):

// Go 1.17+ 使用runtime/cgo.Handle
import "runtime/cgo"

var handleMap sync.Map

//export create_cgo_handle
func create_cgo_handle(obj MyInterface) C.uintptr_t {
    h := cgo.NewHandle(obj)
    return C.uintptr_t(h)
}

//export use_cgo_handle
func use_cgo_handle(h C.uintptr_t) {
    if obj, ok := cgo.Handle(h).Value().(MyInterface); ok {
        obj.MyFunc()
    }
}

//export delete_cgo_handle
func delete_cgo_handle(h C.uintptr_t) {
    cgo.Handle(h).Delete()
}

关键注意事项:

  1. 使用sync.Mapsync.RWMutex保证线程安全
  2. 确保在C端不再需要时释放句柄
  3. 避免在C中长期持有Go指针,防止Go对象被垃圾回收
  4. 对于Go 1.17+,优先使用runtime/cgo.Handle
回到顶部