如何防止在托管代码中创建的功能指针,并传递给非托管代码,从而收集垃圾



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

muparsernet由带有托管C#包装器的C (未托管(DLL组成。在此包装器中,它将指针传递给错误处理程序的初始化库。

即。在解析器类中,我们具有此功能:

    /// <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是此类型。

我的理解是,由于错误处理程序的指针将其传递给未托管代码后未使用,因此收集了垃圾。我如何防止这种情况发生?

基于第一个答复的更多信息:解析器对象是在一个计算例程中创建的,该程序通常在多达8个单独的线程上同时运行。对象是在计算例程中创建并处置的。因此,我不想在任何时候都将解析器对象创建为静态的对象,因为它只能在任何时候使用一个线程。

无法对&lt评论;50声誉,但我没有明确的答案,只有提示。

因此,您是说可以有多个线程,这些线程都称为" muparserlibrary.mupseterrorhandler"?这似乎已经错了。图书馆的规格是否声明Mupseterrorhandler,或者实际上是图书馆的任何部分为"线程安全"?

想象一下这种情况:

  • 线程A设置错误处理程序,启动工作。
  • 线程B设置错误处理程序,开始工作。库中的当前错误处理程序现在对thread-b的错误处理程序进行了引用。
  • 线程B比A。
  • 完成工作
  • A产生错误。
  • 库仍然对B中的错误处理程序的引用,该库现在无效。

从您的示例中,尚不清楚B是否可以比A更早停止,但是如果还有另一种情况使您进入这样的状态,那将会发生。我认为您需要一个库始终使用的更全局错误处理程序。但是,如果您无法跟踪错误的源(线程(,或者库只是用于多线程使用的库,则不会有太大帮助。如果这是围绕本机C dll围绕库的典型的"静态C#包装器",那么我恐怕就是这样。您还需要库状态的几个对象(及其对错误处理程序等的引用(,即每个线程一个。如果该库无法做到这一点,您现在正在做事的方式将无法正常工作。

我最终弄清楚了。

我创建两个类变量:一个用于函数处理程序,一个用于gchandle:

private ErrorFuncType ptrErrHandler; 
private GCHandle gchErrorHandler;

然后使用gchandle在将其传递给未托管的代码之前,以防止功能指针被收集到垃圾:

  ptrErrHandler = this.ErrorHandler;
  this.gchErrorHandler = GCHandle.Alloc(ptrErrHandler);
  MuParserLibrary.mupSetErrorHandler(this.parserHandler, ptrErrHandler);

最后,在类驱动器中,您需要释放GCHANDLE以允许收集垃圾:

  gchErrorHandler.Free();

最新更新