PythonNet踩坑脱坑

没想到距离上次更新已经十几年了,今天心血来潮更新一下PythonNet的使用感受吧

前因:
因为项目需要,得实现C#调用Python脚本,上网逛了一圈发现都在推荐PythonNet,遂决定用这个。
基本需求:
1、Python脚本需要批量按次运行,各脚本间没有业务关联
2、每个脚本的库路径可能不一样
3、需要C#提供一些对象供Python使用,各脚本间使用的对象可能是同一个C#静态对象

最大的坑:
PythonNet实际上在Shutdown中并没有做Finalize。
1、这就导致Py_SetPath无法生效(就是PythonNet的PythonEngine.PythonPath属性赋值),调用后虽然是新的路径,但是仍然按照上一个脚本的路径来找,那自然找不到
2、因为没有真正的Finalize,所以PythonNet在Shutdown的时候会把当前的运行时数据全都序列化,然后在下次PythonEngine.Initialize()反序列化还原这些运行时数据,然后这就引出了一个很莫名其妙的问题。我导入C#对象后,在第二次执行脚本反序列化时提示“已添加了具有相同键的项”。翻了一天PythonNet的代码,根据自己浅薄的理解,简单粗暴的解决了一下上面的两个问题:

既然没有做Finalize,那就手动给来一次,让所有数据都刷新

在Runtime.cs的Initialize里面可以看到有interpreterAlreadyInitialized来判断是否已经做过Initialize,修改方法就是在下面增加Finalize,然后重新来一遍上面的流程

            if (!interpreterAlreadyInitialized)
            {
                Py_InitializeEx(initSigs ? 1 : 0);

                NewRun();

                if (PyEval_ThreadsInitialized() == 0)
                {
                    PyEval_InitThreads();
                }
                RuntimeState.Save();
            }
            else
            {
                if (!HostedInPython)
                {
                    PyGILState_Ensure();
                }

                BorrowedReference pyRun = PySys_GetObject(RunSysPropName);
                if (pyRun != null)
                {
                    run = checked((int)PyLong_AsSignedSize_t(pyRun));
                }
                else
                {
                    NewRun();
                }
                Py_Finalize();
                Py_InitializeEx(initSigs ? 1 : 0);

                NewRun();

                if (PyEval_ThreadsInitialized() == 0)
                {
                    PyEval_InitThreads();
                }
                RuntimeState.Save();
            }


然后这样还不够,这样会导致在后面Shutdown时抛“Python object finalization failed”,原因是当前运行时的run值与实际要Dispose的对象run值不一致,具体为啥没细看,直接把三个异常Handler干掉,位置在Finalizer.cs的DisposeAll(),一共三个HandleFinalizationException

至此,完成项目需求。

PS:这种改法感觉并不安全,但没有那么多闲工夫去了解PythonNet库的底层机制,所以就凑合着这么弄了,有可能会有内存泄露之类的风险,待后续压力测试出问题再说。
PPS:我懒得换成英语去GitHub上面给原作者提Issue,而且我也懒得Fork工程,就这么地吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值