Disposing of COM in multi-threaded environment
I'm trying to properly dispose of a legacy VFP (FoxPro) COM control wrapped in a RCW generated by Visual Studio. The control exposes a Destroy method I should call to allow the control to properly tear itself down. There is a very good chance a method on the control may be executing on a background thread when a request is made to dispose of the COM instance. VFP is a single-threaded apartment model, so when calling Destroy it should just be added to the VFP execution stack.
Calling Destroy would ideally be the right thing to do as it allows the COM instance to clean up some resources. My concern is that instantiating a VFP COM control actually starts up a VFP language runtime instance that the control is hosted in and that instance may be locked up (non-responsive). This COM component exposes functionality in a large enterprise-scale 20-year-old legacy app and I have seen situations where a .NET thread attempting to call a method on this control simply blocks without throwing an error (always caused by bugs in the legacy VFP code). This doesn't happen often, but it is often enough that it prompted me to build an instan开发者_StackOverflow中文版ce manager that runs methods on the VFP COM instance in a background thread and periodically checks to see if that thread is blocked, and if so, destroys the COM instance and thread and restarts a new instance to monitor.
Is this the right way to dispose of the thread that a background method may be executing on?
Should I attempt to get fancier by trying to call the Destroy method to allow the COM control to properly tear down?
if (_vfpThread != null)
{
try
{
if (_vfpThread.IsAlive)
_vfpThread.Abort();
}
catch (ThreadAbortException)
{ }
finally
{
_vfpThread = null;
}
}
if (_vfpInstance != null)
{
Marshal.ReleaseComObject(_vfpInstance);
_vfpInstance = null;
}
When a method call is pending on a VFP-based COM object (which always runs in an STA apartment), invoking any method on that same COM object from another thread will block until the former call returns (exits the apartment). That means, any thread attempting to call Destroy() concurrently will be at the mercy of that first thread. And if that thread doesn't know to exit voluntarily, it could in theory keep the disposing thread blocked indefinitely. So, in other words, there's no direct way to ask the 1st thread to exit the method immediately by calling another method on the COM object from within another thread. Calling _vfpThread.Abort() should work, but the safety of this approach largely depends on the internals of the VFP class. In many cases, due to it being legacy code, it won't have anything like a try/catch/finally section that would allow for a graceful exit, therefore resources may wind up being left unreleased. - BAD!
Another approach would be to set an external flag somewhere (registry, file, whatever), which would be available for reading by that 1st thread from within the method it is executing. That of course requires that the VFP class be aware of having to read the flag from each of its COM-published methods, and act accordingly and quickly.
Also, regarding your code snippet. Catching ThreadAbortException in the code that is aborting a thread only makes sense if the thread executing this code is aborting itself. Which would be pretty awkward, since it could instead just return from the method. (Or, is this thread that is calling _vfpThread.Abort() also potentially being aborted from yet another thread?) In a normal scenario, what you'd need wrapped in a ThreadAbortException catcher is the 1st thread's main code that performs calls to all those business methods on the COM object. Ideally, you'd have this as deep down the stack as the VFP methods themselves where the code would be able to gracefully close all resources/tables etc before re-throwing the exception. And then in the main method that you passed to the ThreadStart, you'd have a similar catcher except it would peacefully return from the method, thereby terminating the thread or releasing it to the thread pool.
Yes, I did understand your code correctly.-Thanks.
Aborting the vfp thread if it does not exit gracefully within 60 seconds is perhaps the only thing you could do. In terms of what Dispose should do - it should try its best to release all unmanaged resources, which are unfortunately hidden from this code as they are used/opened from within the VFP COM class. So, if the COM object is seized, the main thread won't be able to force it to release those resources. Perhaps what you could try doing is wrapping the entire body of the COM business method in a VFP try-catch block and releasing resources/closing tables in the catch section. There's a good chance that the try-catch block would capture the ThreadAbortException caused by calling _vfpThread.Abort() from the main thread.
精彩评论