多线程异常和Dispose。为什么Dispose没有打电话?

时间:2011-04-25 09:52:47

标签: c# multithreading dispose

'using'语句保证对象将被称为Dispose方法。在这个例子中,这没有发生。终结者方法也没有调用。

为什么这一切?当其他线程上的异常发生时,我如何更改代码以保证处理我的对象?

class Program
{
    static void Main(string[] args)
    {
        Thread th1 = new Thread(ThreadOne);
        Thread th2 = new Thread(ThreadTwo);

        th1.Start();
        th2.Start();

        th1.Join();
        th2.Join();
    }

    static void ThreadOne()
    {
        using (LockedFolder lf = new LockedFolder(@"C:\SomeFodler"))
        {
            // some pay load
            Thread.Sleep(5000);
        }
    }

    static void ThreadTwo()
    {
        // some pay load
        Thread.Sleep(1000);
        throw new Exception("Unexpected exception");
    }
}

public class LockedFolder : IDisposable
{
    private const string FILENAME_LOCK = ".lock-file";
    private bool bLocked = false;

    public string FullPath { private set; get; }

    public LockedFolder(string FullPath)
    {
        this.FullPath = FullPath;
        Lock();
    }

    private void Lock()
    {
        // lock our folder
        Console.WriteLine("Lock " + FullPath);

        //CreateLockFile(Path + FILENAME_LOCK);
        bLocked = true;
    }

    private void UnLock()
    {
        if (!bLocked)
        {
            Console.WriteLine("Already UnLocked " + FullPath);
            return; // already unlocked
        }

        Console.WriteLine("UnLock " + FullPath);

        // unlock our folder
        //DeleteLockFile(Path + FILENAME_LOCK);
        bLocked = false;
    }

    #region IDisposable Members

    private bool disposed = false;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    public void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Free managed resources

            }

            // Free unmanaged resource
            UnLock();
        }

        disposed = true;
    }

    ~LockedFolder()
    {
        Dispose(false);
    }

    #endregion
}

输出:

  

\ Visual Studio   2010 \项目\ ExceptionExample \ ExceptionExample \ BIN \调试和GT; ExceptionExample.exe

     

锁定C:\ SomeFodler

     

未处理的异常:System.Exception:   意外的异常在   ExceptionExample.Program.ThreadTwo()   在\ visual studio中   2010 \项目\ ExceptionExample \ ExceptionExample \的Program.cs:行   36点   System.Threading.ThreadHelper.ThreadStart_Context(对象   国家)   System.Threading.ExecutionContext.Run(执行上下文   executionContext,ContextCallback   回调,对象状态,布尔值   ignoreSyncCtx)at   System.Threading.ExecutionContext.Run(执行上下文   executionContext,ContextCallback   回调,对象状态)at   System.Threading.ThreadHelper.ThreadStart()

Outupt无一例外:

  

\ Visual Studio   2010 \项目\ ExceptionExample \ ExceptionExample \ BIN \调试和GT; ExceptionExample.exe   锁C:\ SomeFodler   UnLock C:\ SomeFodler

5 个答案:

答案 0 :(得分:4)

保证;例如,拔出插头或终止进程将不会尊重using。所有保证的是在正常执行中(包括大多数理智的例外),它将调用Dispose()

在您的情况下,您有一个未处理的线程异常;这是一个过程杀手。所有的赌注都是关闭的,因为你的过程现在已经病倒了,而且正在被放下(而且还有它的痛苦)。

如果您希望代码具有行为,则必须确保您没有代理处理异常;线程位于该列表顶部的未处理异常。强烈建议围绕任何线程级代码try / catch

答案 1 :(得分:4)

未处理的异常强制CLR终止进程。对于.NET 4.0,关闭行为略有不同,终结器将在报告异常后运行。但不是早期版本。

您可以通过为AppDomain.CurrentDomain.UnhandledException编写事件处理程序来解决此默认行为。记录或报告异常并调用Environment.Exit()。这允许终结器线程运行并调用你的Unlock()方法。

不要依赖于此,有一些令人讨厌的例外,例如StackOverflow或FEEE,无论如何都会终止这个过程。那么有人绊倒电源线或使用Taskmgr.exe

在头部拍摄你的过程

答案 2 :(得分:1)

原因是当您的进程由于未处理的异常而终止时,终结器不会运行。有关详细信息,请参阅here。您可以强制让终结器运行,以便在另一个线程的未处理异常处理程序中正常关闭您的进程。 当发生未处理的异常时,.NET Framework的策略几乎没有做任何事情,因为不清楚进程处于哪种状态。处理终结器可能是不明智的,因为应用程序状态可能已损坏,并且在最终确定期间也会发生异常,这也会终止终结器线程。净效应是只有一些终结器运行,其余的未经处理。这些后续异常确实使查找应用程序失败的根本原因变得更加困难。

此致,   Alois Kraus

答案 3 :(得分:0)

如果在BackgroundWorker上运行线程,那么抛出的任何异常都将在工作线程中捕获,并将作为线程返回的对象的一部分返回。因此,您无需担心异常转义。

这会产生另一个问题,即您无法在Join上调用BackgroundWorker,但是您可以将Semaphore添加到工作者类,并将计数器设置为0(已阻止) :

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

运行工作人员后添加等待

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

并在您的工作人员代码中添加一个Release,表示您已完成此任务。

  workerFinished.Release() 

答案 4 :(得分:0)

正如Marc所说,一旦你的应用程序有未处理的异常,所有的赌注都差不多了。为了澄清using实际上在做什么,它需要这样的代码:

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

并将其翻译成以下内容:

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

那么当您的应用程序遇到未处理的异常时会发生什么?未处理的异常导致应用程序终止。此终止(或任何意外终止)可能会阻止finally语句中的using阻止正常执行。

您应该始终处理异常,即使在您没有使用try块进行线程调用的情况下也是如此。在您的应用程序咬住灰尘之前,请考虑挂钩AppDomain.UnhandledException事件以清理资源,记录内容等。

修改

刚刚注意到Hans发布了类似AppDomain.UnhandledException的内容,他是对的。任何程序都是这种情况,意外终止会产生意外结果。但是,在您的情况下,正如所建议的那样,不要依赖完整的应用程序执行来完成,尤其是文件资源。相反,考虑编写您的流程以预测甚至预期先前的执行失败。然后,您的应用程序可以根据需要处理不完整的执您可以创建日志来跟踪流程中步骤的进度或标记,并在每次运行时评估它们以解决不正确的执行状态。

另外,作为另一个注释,你的课程(即使考虑到它们只是样本的事实)也不是线程安全的......你不是在保护你的共享资源。