Golang协程中调用Python3

如何在Go中调用Python3的代码不再赘述,网上一大把。但是在协程中多次调用Python函数会panic。
python demo:

# coding: utf-8

def test(a, b):
    return a + b

Goalng demo

package main

import (
	"fmt"
	"sync"

	"github.com/DataDog/go-python3"
)

func init() {
	python3.Py_Initialize()
}

var PyStr = python3.PyUnicode_FromString
var GoStr = python3.PyUnicode_AsUTF8

func main() {
	InsertBeforeSysPath("/usr/local/lib/python3.7/site-packages/")
	hello := ImportModule("/Users/xiangxianzhang/go/src/awesomeProject", "test")

	hi := hello.GetAttrString("test")
	bArgs := python3.PyTuple_New(2)
	python3.PyTuple_SetItem(bArgs, 0, PyStr("xixi"))
	python3.PyTuple_SetItem(bArgs, 1, PyStr("xixi"))
	var wg sync.WaitGroup
	for i :=0; i < 5; i ++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			res := hi.Call(bArgs, python3.Py_None)
			fmt.Printf("[CALL] hi('xixi') = %s\n", GoStr(res))
		}()
	}
	wg.Wait()
	select {
	}
}

// InsertBeforeSysPath will add given dir to python import path
func InsertBeforeSysPath(p string) string {
	sysModule := python3.PyImport_ImportModule("sys")
	path := sysModule.GetAttrString("path")
	python3.PyList_Insert(path, 0, PyStr(p))
	return GoStr(path.Repr())
}

// ImportModule will import python module from given directory
func ImportModule(dir, name string) *python3.PyObject {
	sysModule := python3.PyImport_ImportModule("sys")
	path := sysModule.GetAttrString("path")
	python3.PyList_Insert(path, 0, python3.PyUnicode_FromString(dir))
	return python3.PyImport_ImportModule(name)
}

执行:

 ✘ ⚙  ~/go/src/awesomeProject  go run main.go



[FUNC] hi = &python3.PyObject{ob_refcnt:2, ob_type:(*python3._Ctype_struct__typeobject)(0x4390fa0)}
fatal error: unexpected signal during runtime execution
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x1 addr=0x18 pc=0x421f7e6]

runtime stack:
runtime.throw(0x40e1cd5, 0x2a)
        /usr/local/go/src/runtime/panic.go:1116 +0x72
runtime.sigpanic()
        /usr/local/go/src/runtime/signal_unix.go:704 +0x48c

goroutine 20 [syscall]:
runtime.cgocall(0x40b0170, 0xc000038ee0, 0x0)
        /usr/local/go/src/runtime/cgocall.go:133 +0x5b fp=0xc000038eb0 sp=0xc000038e78 pc=0x40048db
github.com/DataDog/go-python3._Cfunc_PyObject_Call(0xbc14a70, 0xd1772d0, 0x4398138, 0x0)
        _cgo_gotypes.go:4351 +0x4e fp=0xc000038ee0 sp=0xc000038eb0 pc=0x40aaaae
github.com/DataDog/go-python3.(*PyObject).Call.func1(0xbc14a70, 0xd1772d0, 0x4398138, 0x0)
        /Users/xiangxianzhang/go/pkg/mod/github.com/!data!dog/go-python3@v0.0.0-20211102160307-40adc605f1fe/object.go:160 +0xab fp=0xc000038f10 sp=0xc000038ee0 pc=0x40abacb
github.com/DataDog/go-python3.(*PyObject).Call(0xbc14a70, 0xd1772d0, 0x4398138, 0x0)
        /Users/xiangxianzhang/go/pkg/mod/github.com/!data!dog/go-python3@v0.0.0-20211102160307-40adc605f1fe/object.go:160 +0x3f fp=0xc000038f40 sp=0xc000038f10 pc=0x40ab49f
main.main.func1(0xc0000aa030, 0xbc14a70, 0xd1772d0)
        /Users/xiangxianzhang/go/src/awesomeProject/main.go:33 +0x76 fp=0xc000038fc8 sp=0xc000038f40 pc=0x40ad736
runtime.goexit()
        /usr/local/go/src/runtime/asm_amd64.s:1374 +0x1 fp=0xc000038fd0 sp=0xc000038fc8 pc=0x4064921
created by main.main
        /Users/xiangxianzhang/go/src/awesomeProject/main.go:31 +0x205

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc0000aa038)
        /usr/local/go/src/runtime/sema.go:56 +0x45
sync.(*WaitGroup).Wait(0xc0000aa030)
        /usr/local/go/src/sync/waitgroup.go:130 +0x65
main.main()
        /Users/xiangxianzhang/go/src/awesomeProject/main.go:37 +0x225

关于这个问题我在github上提过issue。这是由于直接使用go来调用python函数,不会产生GIL锁,这可能会导致竞争条件,从而导致致命的运行时错误,并且很可能出现分段错误导致整个 Go 应用程序崩溃。
解决方案:

func main() {
	InsertBeforeSysPath("/usr/local/lib/python3.7/site-packages/")
	hello := ImportModule("/Users/xiangxianzhang/go/src/awesomeProject", "test")

	hi := hello.GetAttrString("test")
	bArgs := python3.PyTuple_New(2)
	python3.PyTuple_SetItem(bArgs, 0, PyStr("xixi"))
	python3.PyTuple_SetItem(bArgs, 1, PyStr("xixi"))
	state := python3.PyEval_SaveThread()
	var wg sync.WaitGroup
	for i :=0; i < 5; i ++ {
		wg.Add(1)
		go func() {
			defer wg.Done()
			_gstate := python3.PyGILState_Ensure()
			defer python3.PyGILState_Release(_gstate)
			res := hi.Call(bArgs, python3.Py_None)
			fmt.Printf("[CALL] hi('xixi') = %s\n", GoStr(res))
		}()
	}

	wg.Wait()
	python3.PyEval_RestoreThread(state)
	python3.Py_Finalize()
	select {
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值