我在我的应用程序中使用了一个名为muParserNET的库。 muParserNET是一个函数解析库,我的应用程序从不同的线程多次调用它。
muParserNET由带有托管C#包装程序的C ++(非托管)dll组成。在此包装器中,它将在初始化时将指向错误处理例程的指针传递给非托管库。
即在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个单独的线程上运行。在计算例程中创建并处理对象。因此,我不想将解析器对象创建为静态对象,因为它会限制我一次只能使用一个解析器来使用一个线程。
答案 0 :(得分:0)
不能以<50个声誉发表评论,但是我没有明确的答案,只有提示。
所以您说的是可以有多个线程都被称为“ MuParserLibrary.mupSetErrorHandler”?那似乎已经错了。库的规范是否声明mupSetErrorHandler或实际上库的任何部分为“线程安全”?
想象一下这种情况:
在您的示例中,尚不清楚B是否可以比A提前停止,但是如果还有另一种可能使您进入这种状态,则将发生这种情况。 我认为您需要该库始终使用的全局错误处理程序。但是,如果您无法跟踪错误的源(线程),或者该库不是供多线程使用的,则它不会有太大帮助。 如果这是典型的“围绕本机C DLL的静态类C#包装器”库内容,那么恐怕就是这种情况。 您还需要几个库状态的对象(及其对错误处理程序的引用等),即每个线程一个。如果该库无法做到这一点,那么您的工作方式将无法正常工作。
答案 1 :(得分:0)
我终于弄清楚了。
我创建了两个类变量:一个用于函数处理程序,一个用于GChandle:
private ErrorFuncType ptrErrHandler;
private GCHandle gchErrorHandler;
然后使用GCHandle防止在将函数指针传递给非托管代码之前被垃圾回收:
ptrErrHandler = this.ErrorHandler;
this.gchErrorHandler = GCHandle.Alloc(ptrErrHandler);
MuParserLibrary.mupSetErrorHandler(this.parserHandler, ptrErrHandler);
最后,您需要在类析构函数中释放GCHandle以使其收集垃圾:
gchErrorHandler.Free();