项目需求,复用了一段之前写过的Go代码,利用Go 1.5的新功能导出了一个可以给C语言使用的动态库。由于目前Go的结构体还不支持直接导出到C,因此使用了interface{}作为过渡。结构体以及导出函数定义如下
type Handler struct{
stream *Stream
p *Parser
}
//export NewHandler
func NewHandler() interface{}{
return Handler{
//成员变量初始化
}
}
//export Func1
func Func1(handler interface{}){
if h, ok := handler.(Handler);ok{
//访问stream成员
foo1(h.stream)
}
}
//export Func2
func Func2(handler interface{}){
if h, ok := handler.(Handler);ok{
//访问stream成员
foo2(h.stream)
}
}
按照如上写法,在C代码中依次调用,在执行Func1时还算ok,但是在执行到Func2时,则报了h.stream内存地址非法的错误,暂时还不明白stream在哪被gc了,还望各位赐教。
另外,如下代码则没问题。
//export NewHandler
func NewHandler() interface{}{
return &Handler{
//成员变量初始化
}
}
//export Func1
func Func1(handler interface{}){
if h, ok := handler.(*Handler);ok{
//访问stream成员
foo1(h.stream)
}
}
//export Func2
func Func2(handler interface{}){
if h, ok := handler.(*Handler);ok{
//访问stream成员
foo2(h.stream)
}
}
更新1: 2015.12.30 测试结果表明,改为指针也不能有效解决该问题,仍然会出现内存地址非法情况,看来是Go里面的对象导出给C使用后,gc对该对象的跟踪出了问题,导致在C里边还在使用该对象时却被Go里边回收了。
更新2: 由于是被Go内部gc,考虑在Go代码增加一对象池防止对象被gc,另外考虑到给不熟悉Go的使用C的童鞋,此处返回unsafe.Pointer代替之前的interface{},更改后的逻辑如下
var(
obj_pool = map[unsafe.Pointer]*Handler{}
lock = make(chan bool, 1)
)
type Handler struct{
stream *Stream
p *Parser
}
//export NewHandler
func NewHandler() unsafe.Pointer{
p:= &Handler{
//成员变量初始化
}
lock <- true
defer func() { <-lock }()
obj_pool[unsafe.Pointer(p)] = p
return unsafe.Pointer(p)
}
//其他导出函数...
//......
//export ReleaseHandler
func ReleaseHandler(handler unsafe.Pointer){
lock <- true
defer func() { <-lock }()
if _, ok := obj_pool[handler]; ok {
delete(obj_pool, handler)
}
}
增加对象池后,之前的Go导出对象被gc的问题基本上已经没看到了,相当于变相在Go里面增加对象引用以防止被gc。此外,为了防止资源泄露,采用对象池的方法处理后,自然需要添加一个释放对象的逻辑,于是增加ReleaseHandler方法。