gpt4 book ai didi

c# - 如何防止在托管代码中创建并传递给非托管代码的函数指针被垃圾收集

转载 作者:太空宇宙 更新时间:2023-11-03 14:41:25 24 4
gpt4 key购买 nike

我在我的应用程序中使用了一个名为 muParserNET 的库。 muParserNET 是一个函数解析库,我的应用程序从不同的线程多次调用它。

muParserNET 由一个 C++(非托管)dll 和一个托管 C# 包装器组成。在此包装器中,它在初始化时将指向错误处理例程的指针传递给非托管库。

即在 Parser 类中,我们有这个函数:

    /// <summary>
/// Error handler. It loads the ParserError exception.
/// </summary>
private void ErrorHandler()
{
IntPtr ptrMessage = MuParserLibrary.mupGetErrorMsg(this.parserHandler);
string message = Marshal.PtrToStringAnsi(ptrMessage);

IntPtr ptrToken = MuParserLibrary.mupGetErrorToken(this.parserHandler);
string token = Marshal.PtrToStringAnsi(ptrToken);

string expr = this.Expr;
ErrorCodes code = (ErrorCodes)MuParserLibrary.mupGetErrorCode(this.parserHandler);
int pos = MuParserLibrary.mupGetErrorPos(this.parserHandler);

// lança a exceção
throw new ParserError(message, expr, token, pos, code);
}

这是托管代码中解析器对象的初始化。它发生在这个函数的最后一行:

    public Parser()
{
// inicializa o parser
this.parserHandler = MuParserLibrary.mupCreate(0);

// inicializa o dicionário com as variáveis
this.vars = new Dictionary<string, ParserVariable>();

// inicializa as listas de delegates
this.identFunctionsCallbacks = new List<ParserCallback>();
this.funcCallbacks = new Dictionary<string, ParserCallback>();

this.infixOprtCallbacks = new Dictionary<string, ParserCallback>();
this.postfixOprtCallbacks = new Dictionary<string, ParserCallback>();
this.oprtCallbacks = new Dictionary<string, ParserCallback>();

// inicializa o delegate de factory
this.factoryCallback = new ParserCallback(new IntFactoryFunction(this.VarFactoryCallback));

// ajusta a função de tratamento de erros
MuParserLibrary.mupSetErrorHandler(this.parserHandler, this.ErrorHandler);
}

在偶尔运行这段代码时,在调用评估函数时(所以在对象初始化之后的某个时间)我得到这个错误:

A callback was made on a garbage collected delegate of type 'muParserNET!muParserNET.ErrorFuncType::Invoke'

ErrorFuncType 是上面使用 MuParserLibrary.mupSetErrorHandler 传递的 this.ErrorHandler 的类型。

我的理解是,由于错误处理函数在其指针传递给非托管代码后未被使用,因此它会被垃圾回收。我怎样才能防止这种情况发生?

更多信息基于第一个回复:解析器对象是在一个计算例程中创建的,该例程通常可以在最多 8 个独立的线程上同时运行。该对象在计算例程中创建和处理。出于这个原因,我不想将解析器对象创建为静态的,因为它会限制我在任何时候只能使用一个线程使用解析器。

最佳答案

无法以 < 50 声望发表评论,但我没有明确的答案,只有提示。

所以你是说可以有多个线程都调用“MuParserLibrary.mupSetErrorHandler”?这似乎已经错了。库的规范是否将 mupSetErrorHandler 或实际上库的任何部分声明为“线程安全”?

想象一下这个场景:

  • 线程 A 设置错误处理程序,开始工作。
  • 线程 B 设置错误处理程序,开始工作。库中的当前错误处理程序现在引用了线程 B 的错误处理程序。
  • 线程 B 比 A 更早完成工作。
  • A 产生错误。
  • 库仍然引用 B 的错误处理程序,但现在无效。

从您的示例中不清楚 B 是否可以比 A 更早停止,但如果有另一种情况可能使您进入这种状态,那么就会发生这种情况。我认为您需要库始终使用的更全局的错误处理程序。但是,如果您无法跟踪错误的来源(线程),或者库不是为多线程使用而设计的,那么它就没有多大帮助。如果这是典型的“围绕 native C DLL 的静态类 C# 包装器”库,恐怕就是这种情况。您还需要库状态的几个对象(及其对错误处理程序等的引用),即每个线程一个。如果那个图书馆不能做到这一点,那么您现在做事的方式将行不通。

关于c# - 如何防止在托管代码中创建并传递给非托管代码的函数指针被垃圾收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56771249/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com