我在我的应用程序中使用了一个名为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();