Multithreading exception and Dispose. Why Dispose didn't call?

陌路散爱 提交于 2019-12-04 15:30:39

The unhandled exception forces the CLR to terminate the process. Shutdown behavior is slightly different for .NET 4.0, the finalizer will run after reporting the exception. But not in earlier versions.

You can work around this default behavior by writing an event handler for AppDomain.CurrentDomain.UnhandledException. Log or report the exception and call Environment.Exit(). That allows the finalizer thread to run and call your Unlock() method.

Do not rely on this, there are nasty exceptions like StackOverflow or FEEE that terminate the process anyway. So does somebody tripping over the power cord or shooting your process in the head with Taskmgr.exe

Nothing is guaranteed; pulling the plug out or terminating a process won't respect using, for example. All that is guaranteed is that in normal execution (which includes most sane exceptions), it will call Dispose().

In your case, you have an unhandled thread exception; that is a process killer. All bets are off, since your process is now sickly and in the process of being put down (and out of it's misery).

If you want code to behave, you must ensure ou don't have process-killing exceptions; unhandled exceptions on threads being at the top of that list. A try/catch around any thread-level code is highly recommended.

The reason is when your process terminates due to an unhandled exception the finalizers are not run. See here for more details. You can force to let your finalizers to run with the trick to shutdown your process normally within the unhandled exception handler from another thread. The policy of the .NET Framework is to do pretty much nothing when an unhandled exception occurs because it is not clear in which state the process is left. It may be unwise to process the finalizers since application state can be corrupt and during finalization also exceptions occur which would kill the finalizer thread as well. The net effect is that only some finalizers did run and the rest is left unprocessed. These followup exceptions do make it harder to find the root cause why the application did fail.

Yours, Alois Kraus

If you run your threads on a BackgroundWorker then any exceptions that are thrown will be caught in the worker thread and will be returned as part of the object returned by the thread. Thus you won't need to worry about your exceptions escaping.

This creates another problem, that being that you cannot call Join on a BackgroundWorker, however you can add a Semaphore to the worker class with the counter set to 0 (blocked):

  private Semaphore workerFinsished = new Semaphore(0, 1);

add a wait after you run your worker,

  public void Join() { workerFinished.WaitOne(); }

and add a Release in your worker code where you want to signal that you are done.

  workerFinished.Release() 

As Marc said, once your app has unhandled exceptions, all bets are pretty much off. To clarify on what using is actually doing, it takes code like this:

using(var myDisposableObject = GetDisposableObject()) {
    // Do stuff with myDisposableObject
}

and translates it into something like this:

MyDisposableObject myDisposableObject;
try {
    myDisposableObject = GetDisposableObject();
    // Do stuff with myDisposableObject
}
finally {
    if(myDisposableObject != null) {
        myDisposableObject.Dispose();
    }
}

So what happens when your application encounters an unhandled exception? The unhandled exception causes the application to be terminated. This termination (or any unexpected termination) can prevent the finally block from your using statement from executing properly.

You should always handle your exceptions, even in your case where you're not surrounding your threaded calls with try blocks. Consider hooking onto the AppDomain.UnhandledException event to clean up resources, log stuff, etc. before your application bites the dust.

EDIT

Just noticed Hans posted something similar regarding AppDomain.UnhandledException and he's right. It's been the case with any program that unexpected terminations can yield unexpected results. However, in your case, as has been suggested, don't rely on the full execution of your application to complete, especially with file resources. Rather, consider writing your process to anticipate, even expect, prior execution failures. Then your application can address incomplete executions as necessary. You can create logs to track the progress or mark of steps in your process and evaluate them on each run to address incorrect execution states.

Also, as another note, your classes (even considering the fact they're just samples) are not thread safe...you're not protecting your shared resources.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!