簡體   English   中英

為什么 finally 塊中的代碼不執行?

[英]Why code in finally block doesn't execute?

如果除了主線程執行代碼之外,finally 塊似乎不會執行。 在這種情況下是否可以最終強制執行?

環境:VS 2010,.Net Framework 4.0.3

class Program
{
    static void Main(string[] args)
    {
        var h = new AutoResetEvent(false);

        ThreadPool.QueueUserWorkItem(
            obj => TestProc(h));

        h.WaitOne();
    }

    private static void TestProc(EventWaitHandle h)
    {
        try
        {
            Trace.WriteLine("Try");
            h.Set();
        }
        catch(Exception)
        {
            Trace.WriteLine("Catch");
        }
        finally
        {
            Thread.Sleep(2000);
            Trace.WriteLine("Finally");
        }
    }
}

更新:

我在 MSDN 中找到了有關該案例的提及和解釋:

ThreadAbortException 類http://msdn.microsoft.com/en-us/library/system.threading.threadabortexception.aspx

當調用 Abort 方法來銷毀線程時,公共語言運行時會引發 ThreadAbortException。 ThreadAbortException 是一種可以被捕獲的特殊異常,但它會在 catch 塊結束時自動再次引發。 引發此異常運行時會在結束線程之前執行所有 finally 塊 因為線程可以在 finally 塊中進行無限計算或調用 Thread.ResetAbort 來取消中止,所以不能保證線程會永遠結束 如果要等到中止的線程結束,可以調用 Thread.Join 方法。 Join 是一個阻塞調用,直到線程實際停止執行才會返回。

筆記:

當公共語言運行時 (CLR) 在托管可執行文件中的所有前台線程都結束后停止后台線程時,它不會使用 Thread.Abort。 因此,您不能使用 ThreadAbortException 來檢測 CLR 何時終止后台線程。


前台和后台線程http://msdn.microsoft.com/en-us/library/h339syd0.aspx

當運行時因為進程關閉而停止后台線程時,線程中不會拋出異常。 但是,當線程因為 AppDomain.Unload 方法卸載應用程序域而停止時,前台和后台線程都會拋出 ThreadAbortException。


那么為什么在應用程序結束時 CLR 不使用 AppDomain.Unload 方法在主進程結束(殺死)之前卸載應用程序域? 因為http://msdn.microsoft.com/en-us/library/system.appdomain.unload.aspx

當線程調用 Unload 時,目標域被標記為卸載。 專用線程嘗試卸載域,域中的所有線程都被中止。 如果一個線程沒有中止,例如因為它正在執行非托管代碼,或者因為它正在執行一個 finally 塊,那么一段時間后,在最初調用 Unload 的線程中會拋出一個CannotUnloadAppDomainException。 如果無法中止的線程最終結束,則不會卸載目標域。 因此,在 .NET Framework 2.0 版中,域不保證卸載,因為它可能無法終止正在執行的線程。

結論:在某些情況下,我需要考慮我的代碼是在后台線程還是前台線程中執行? 在應用程序主線程結束所有工作之前,我的代碼有可能沒有完成嗎?

您的代碼正在后台線程中運行。 當您設置AutoResetEvent ,您的單個前台線程將終止(當您到達Main方法的末尾時)並且該進程“立即”被拆除。

事實上,我認為你的finally塊很可能開始執行,但是你做的第一件事是休眠兩秒鍾,進程在它到達你的WriteLine調用之前退出。

如果您的Main方法仍在運行,或者任何其他前台線程使進程保持活動狀態,您會看到您的finally塊正常完成。 這實際上不是“最終在其他線程上”的問題——而是“進程僅在有前台線程時才保持活動狀態”的問題。

您可以防止 main 方法退出,直到 finally 執行。 有許多可能的方法。

  • 您可以使用同步來實現這一點。 例如使用 ResetEvent,類似於您已經在做的事情,或者顯式地創建一個線程並加入它。

  • 您可以在Main方法的末尾添加一個簡單的 sleep 或 readline:

     h.WaitOne(); Console.ReadLine();

然后用戶可以控制程序何時退出。

  • 您可以使用非后台線程而不是線程池中的線程。 然后程序不會退出,直到線程也終止。 如果您希望程序在線程完成之前不終止,這可能是最好和最簡單的選擇。

我有一段時間遇到了同樣的問題,經過幾次不同的嘗試,以下方法奏效:參考鏈接https://docs.microsoft.com/en-us/visualstudio/code-quality/ca2124?view=vs-2019

本質上它在你的 Main() 中。 我一直在使用它,它按預期為我清理了一切。

try { try {} finally {} } catch {}

這樣你的最終清理代碼就會運行,它仍然會捕獲任何異常。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM