我有一个web api服务器,基本上响应2个请求:停止和启动。
启动时会执行几项操作,例如,初始化每X秒执行一次方法的计时器,停止时它会停止计时器。
为了实现这一点,我创建了一个处理操作逻辑的单例类。 (它需要是单例,因此对于所有请求,定时器和更多变量只存在一次)。
服务器运行正常,但最近我在访问Globalconfiguration.AppSettings时遇到了AccessViolationException,以便从我的webconfig文件中检索一个值。
我通过查看我的日志发现了调用单例类终结器,即使我没有重启服务器。
终结器调用我经常使用的方法并在其他方案中正常工作,并且此方法使用引发异常的GlobalConfiguration类。
我试图找到原因而没有成功。
所以基本上这里有两个错误: 1.为什么终结者会突然出现?服务器可以运行一周。 2. AccessViolationException。
也许错误是相关的?如果我的应用程序内存以某种方式被清除,它会导致调用终结器和访问GlobalConfiguration的异常吗?只是一个理论......
或许也许我不能正确处理单身人士?但是,在阅读了c#web服务器中的静态变量之后,我发现在应用程序存在时它们应该存在,因此这个bug可能与处理单例无关。我确实处理好了 - 单例有一个私有静态字段,它保存实际的实例,初始化通过锁定和双重检查来防止多个线程创建单例。
我的方法好吗?你看到我在这种行为中没有预料到的任何可能的错误,或者你知道.net框架的任何行为都可能导致我的静态单例被破坏吗?
答案 0 :(得分:6)
关于为什么终结器被调用的第一个问题,明显的解释是它没有任何活动的GC根,这导致GC将它放在最终化队列上。至于为什么GC没有找到对象的活动根,有两种可能性:
在默认配置中,IIS每隔1740分钟(29小时)自动重新启动每个应用程序池,这反过来会导致应用程序的当前应用程序域被卸载。在每个应用程序域中存在.net静态变量,这意味着当卸载应用程序域时,不再有任何活动的GC根到单例,这反过来意味着单例符合垃圾收集和扩展终结的条件。
关于AVE,你需要记住,当你的终结者被执行时everything you know is wrong。当你的终结器被执行时,GlobalConfiguration对象也可能已被GC /完成,并且它对web.config的任何处理都可能已被破坏。
同样重要的是要注意,默认情况下,当IIS回收您的应用程序池时,它会在实际重新创建应用程序池之前等待下一个HTTP请求。从资源利用率的角度来看,这很好,因为这意味着如果您的应用程序没有收到任何请求,它将不会被加载到内存中,但是在您的情况下,这也意味着在您的应用程序收到HTTP请求之前,您的任何计时器都不会存在。
在解决此问题方面,您有几个选择:
禁用IIS的自动重启。这解决了您的直接问题,但它提出了如果服务器由于某些其他原因重新启动会发生什么的问题。您的应用程序是否有某种方法可以持久保存状态(例如在数据库中),以便如果以前启动它将在联机时继续运行?
如果是这样,您还需要为应用程序启用自动启动和预加载,以便IIS自动加载您的应用程序,而无需进行外部HTTP请求。有关详细信息,请参阅this blog post。
IIS之外的主机。 IIS主要用于托管网站而不是后台服务,因此您可以简单地切换到使用Windows服务来托管您的应用程序。使用Owin,您甚至可以在服务中托管现有的Web API,因此您需要进行最少的代码更改。