如何解决垃圾收集时间过早
我正在研究开发跨平台代码,并在对Windows和Linux中的退出代码进行了一些研究之后,将下面的类拼凑在一起,以使控制台应用程序保持活动状态。但是,关闭后会收到错误消息:
进程终止。对类型为“ Bot!Bot.Extensions.Environment.SignalHandler + SetConsoleCtrlEventHandler :: Invoke”的垃圾收集委托进行了回调。
internal interface ISignalHandler
{
void Set();
void Wait();
void Exit();
}
internal class SignalHandler : ISignalHandler
{
private readonly ManualResetEvent _resetEvent = new ManualResetEvent(false);
private readonly SetConsoleCtrlHandler _setConsoleCtrlHandler;
private bool _disposed;
public SignalHandler()
{
if (!NativeLibrary.TryLoad("Kernel32",typeof(Library).Assembly,null,out var kernel)) return;
if (NativeLibrary.TryGetExport(kernel,"SetConsoleCtrlHandler",out var handler))
_setConsoleCtrlHandler =
(SetConsoleCtrlHandler) Marshal.GetDelegateForFunctionPointer(handler,typeof(SetConsoleCtrlHandler));
}
public void Set()
{
if (_setConsoleCtrlHandler == null) Task.Factory.StartNew(UnixSignalHandler);
else _setConsoleCtrlHandler(WindowsSignalHandler,true);
}
public void Wait()
{
_resetEvent.WaitOne();
}
public void Exit()
{
_resetEvent.Set();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void UnixSignalHandler()
{
UnixSignal[] signals =
{
new UnixSignal(Signum.SIGHUP),new UnixSignal(Signum.SIGINT),new UnixSignal(Signum.SIGQUIT),new UnixSignal(Signum.SIGABRT),new UnixSignal(Signum.SIGTERM)
};
UnixSignal.WaitAny(signals);
Exit();
}
private bool WindowsSignalHandler(WindowsCtrlType signal)
{
switch (signal)
{
case WindowsCtrlType.CtrlCEvent:
case WindowsCtrlType.CtrlBreakEvent:
case WindowsCtrlType.CtrlCloseEvent:
case WindowsCtrlType.CtrlLogoffEvent:
case WindowsCtrlType.CtrlShutdownEvent:
Exit();
break;
default:
throw new ArgumentOutOfRangeException(nameof(signal),signal,null);
}
return true;
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing) _resetEvent.Dispose();
_disposed = true;
}
private delegate bool SetConsoleCtrlHandler(SetConsoleCtrlEventHandler handlerRoutine,bool add);
private delegate bool SetConsoleCtrlEventHandler(WindowsCtrlType sig);
private enum WindowsCtrlType
{
CtrlCEvent = 0,CtrlBreakEvent = 1,CtrlCloseEvent = 2,CtrlLogoffEvent = 5,CtrlShutdownEvent = 6
}
}
据我所知,_setConsoleCtrlHandler收集得太早了,但我无法确定如何防止这种情况的发生。即使在分配它后不久调用GC.KeepAlive(_setConsoleCtrlHandler),它仍然会产生错误。
解决方法
您需要为WindowsSignalHandler
创建一个类范围的变量:
private readonly SetConsoleCtrlEventHandler
_windowsSignalHandler = WindowsSignalHandler;
然后,将其传递给您的方法调用:
_setConsoleCtrlHandler(_windowsSignalHandler,true);
这将确保您的回调引用不会被收集,因为您在对象中保留了对它的引用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。