返回页首
并发问题
挂钩的 SetErrorInfoWithExceptionLogging 函数应当是线程安全。首先,您可能希望将 .NET 对象创建和日志记录放在自定义的 SetErrorInfoWithExceptionLogging 函数中,并且使用临界区来防止并发访问。但是,请考虑以下事件序列。首先,线程 A 导致错误并进入 SetErrorInfoWithExceptionLogging,从而获得一个临界区。当线程 A 执行时,它试图获得由线程 B 拥有的另一个资源,因此它等待该资源可用。当线程 B 拥有该资源时,它会生成错误。然后,在线程 B 上调用 SetErrorInfoWithExceptionLogging 函数,以阻止获得该临界区的企图。
结果:发生死锁!SetErrorInfoWithExceptionLogging 中的代码不会直接尝试获得可能被其他线程拥有的其他资源,但是,如果需要在它内部创建 .NET 对象,则会发生多个事件。例如,在创建 .NET 对象时,系统可能需要加载 DLL。这意味着在该时间间隔获得加载程序锁,这会造成发生死锁的可能。如果在 SetErrorInfoWithExceptionLogging 中创建了 .NET 对象并使用它们来发布异常,则会从单线程单元 (STA) 向多线程单元 (MTA) 进行跨单元调用。这可能造成出现重入情况,其中,SetErrorInfoWithExceptionLogging 被在某个 STA 中调用,并且挂钩函数对另一个单元进行传出 COM 调用,这会导致 COM 库进入 STA 消息循环。当另一个针对该 STA 中承载的对象的 COM 方法调用进入时,它将被接受,因为我们处于消息循环中。在处理该新请求期间,SetErrorInfoWithExceptionLogging 函数被再次调用。
由于存在上述并发问题,因此 SetErrorInfoWithExceptionLogging 函数尽可能少地完成工作。它只是向从 ManagedExceptionLoggerTask 辅助线程中访问的队列中添加异常。ManagedExceptionLoggerTask 对象执行异常的日志记录,这将完成创建托管的 ExceptionPublisher 对象和发布异常的重要工作。该线程不会遇到相同的锁定问题,从而使其成为发布异常的好地方。
返回页首
异常队列
为了尽可能减少阻塞问题,创建了一个新的队列对象。托管异常是在先入先出基础上添加和移除的。这些异常将由另一个辅助线程从该队列中移除,然后使用异常管理应用程序块发布。
该队列内部存储的对象的类型不能是 .NET 异常接口指针,因为该异常需要从辅助线程中跨单元访问。例如,自定义挂钩函数可以在 STA 上调用,但是辅助线程使用 MTA。在 ASP 应用程序中,每个客户端都将在它自己的 STA 上调用。
延伸阅读
文章来源于领测软件测试网 https://www.ltesting.net/