Pythonnet - 内存管理错误

问题描述 投票:0回答:1

环境

  • Pythonnet版本:3.0.2
  • Python版本:3.9
  • 操作系统:Windows 10
  • .NET 运行时:.NET Framework 4.8

详情

我正在尝试通过 .NET Framework DLL 打开一个大文件并访问 python 中的数据。

场景:

  • Pythonnet 已加载到我执行这些操作的模块内。
  • C# 对象是 Python 类的属性。
  • python 类实现了 _del_() 方法,在该方法中我必须在销毁对象之前调用 C# 类中的一个方法。
  • 我在 C# 析构函数中添加了日志打印,以了解 CLR 何时调用它。

错误

  • 在某些情况下,CLR 在执行 python 方法的 _del_() 之前记录析构函数调用,所以这没什么大不了的,因为我必须调用 C# 端的方法也在析构函数中调用,我通过异常处理来处理以下事实:当执行 _del_() 时,C# 对象不再存在。 可以解决,但不太好(参见示例)
  • 在其他情况下,Python GC 在 Pythonnet.Runtime.Dll 设法处理内存之前释放内存,因此在函数 Python.Shutdow() 中(如果您熟悉代码,则在此处尝试获取指针)您正在尝试摆脱已经释放的内存区域(抱歉,如果这是一个粗略的解释,但我没有时间和资源来对这个问题进行完整的逆向工程)。我通过在 Python.Shoutdown() 函数中到处添加 try 和 catch 并检查空指针来解决这个问题。 非常糟糕

如果比我对这个工具有更多经验的人看到我正在尝试做的Python方面的任何问题,我会很乐意修复我的代码,否则如果你认为这可能是一个真正的问题,我会用我的更正来提出拉取请求这样每个人都可以从中受益。

更新添加的代码

  • Python 类代码:
import pythonnet
from pythonnet import load
load("netfx")
import clr
from time import time
try:
    clr.AddReference('FileManager.Library')
    import FileManager.Library
except System.IO.FileNotFoundException:
    print("[File loader] DLL not found!")

class FileLoader:
    def __init__(self, filePath):
        self.dataLoader = FileManager.Library.FileReader(filePath)
        self.isClosed = False

    def load_data(self):
        return self.dataLoader.GetDataPoints()
    
    def close(self):
        self.dataLoader.Close()
        self.isClosed = True

    def __del__(self):
        print(f"[FileLoader - {time()}] Freeing memory!", flush=True)

        if not self.isClosed:
            self.close()
  • Python 主要:
from FileManager.FileLoader import FileLoader


if __name__ == "__main__":
    fl = FileLoader("data.csv")
    data = fl.load_data()
    print("Done!")
  • C# 类库代码:
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace FileManager.Library
{
    public class FileReader: IDisposable
    {
        private string filePath;
        private int objectId;
        private bool isDisposed = false;
        private List<DataPoint> fileContent;
        private DateTimeOffset dto = new DateTimeOffset(DateTime.UtcNow);

        ~FileReader() {
            if(!isDisposed)
                Dispose();

            System.Console.WriteLine($"[C# -  {dto.ToUnixTimeMilliseconds().ToString()} ] Deleting {objectId}");
        }

        public FileReader(string filePath)
        {
            this.filePath = filePath;
            fileContent = new List<DataPoint>();
            objectId = this.GetHashCode();
            LoadFile();
        }

        private void LoadFile()
        {

            if (!File.Exists(filePath))
            {
                throw new FileNotFoundException($"Cannot open file{filePath}");
            }

            const Int32 BufferSize = 128;
            using (var fileStream = File.OpenRead(filePath))
            using (var streamReader = new StreamReader(fileStream, Encoding.UTF8, true, BufferSize))
            {
                String line;
                while ((line = streamReader.ReadLine()) != null)
                {
                    var data = line.Split(',');
                    var dataPoint = new DataPoint((int)Convert.ToDouble(data[0]), Convert.ToDouble(data[1]));
                    fileContent.Add(dataPoint);
                }
                fileStream.Close();
            }
            
            return;
        }

        public DataPoint GetDataPoint(int i)
        {
            return fileContent[i];
        }

        public List<DataPoint> GetDataPoints()
        {
            return fileContent;
        }

        public void Dispose() {
            
            if (isDisposed) return;
            System.Console.WriteLine($"[C# - {dto.ToUnixTimeMilliseconds().ToString()}] Disoposing Obj {objectId}");
            isDisposed = true;
        }

        public void Close() {
            System.Console.WriteLine("Dummy things That I have to do before closing");
        }

    }
}

使用调试器运行上面的 python main 工作正常(时间戳也按正确的顺序),但如果没有调试器,我总是得到以下错误:

Dummy things That I have to do before closing
Done!
[C# - 1698683227427] Disoposing Obj 26765710
[C# -  1698683227427 ] Deleting 26765710
[FileLoader - 1698683227.5009353] Freeing memory!
Exception ignored in: <function FileLoader.__del__ at 0x00000158F00920D0>
Traceback (most recent call last):
  File "FileLoader.py", line 30, in __del__
TypeError: 'MethodObject' object is not callable

我哪里错了?我该如何解决它?
注1:显然我不想尝试抓住它我希望它正常工作
注 2: C# 中的相同 main 完全没有错误

python c# memory-leaks binding python.net
1个回答
0
投票

您似乎遇到了一个问题,即在 C# 析构函数之后调用 Python 垃圾收集器(

__del__
函数),导致错误,因为 C# 对象已被释放。发生这种情况是因为 Python 和 .NET 具有不同的内存管理模型。您可以在 Python 代码中显式调用
gc.collect()
,以确保在 C# 析构函数之前调用 Python 中的
__del__
方法。在Python中,可以使用
gc.collect()
方法来手动激活垃圾收集。

import pythonnet
from pythonnet import load
import gc  # Import the gc module

# ... Your other imports ...

class FileLoader:
    def __init__(self, filePath):
        self.dataLoader = FileManager.Library.FileReader(filePath)
        self.isClosed = False

    # ... Your other methods ...

    def __del__(self):
        print(f"[FileLoader - {time()}] Freeing memory!", flush=True)

        if not self.isClosed:
            self.close()

        # Force a garbage collection here
        gc.collect()
© www.soinside.com 2019 - 2024. All rights reserved.