有没有办法知道一个线程是否因为程序终止而被中止?

时间:2016-05-31 18:02:19

标签: c# .net multithreading

是否有一个事件会告诉我当前正在运行的线程是否在中途中止,不是因为用户调用了Abort而是因为程序正在终止?

我需要关闭每个关闭的线程以及有关线程Id的信息。

我希望它会成为AppDomainUnload事件,但the documentation对此说nilch。

This page on the MSDN说:

当运行时停止后台线程因为进程正在关闭时,线程中不会抛出异常。但是,当线程因AppDomain.Unload方法卸载应用程序域而停止时,前台和后台线程都会抛出ThreadAbortException。

为了测试这个事实,我编写了这个小程序,它产生两个长时间运行的线程,其中一个是前台线程而另一个是后台线程,并立即继续退出当前进程,从而尝试卸载当前进程应用程序域。

这应该不仅调用DomainUnload处理程序,还试图中止两个正在运行的线程,并向它们发送ThreadAbortException

但这一切都不会发生。

输出窗口显示以下输出:

[#10] [Foreground]Starting to do stuff.
The thread 0x183c has exited with code 0 (0x0).
[#9] [Worker]Starting to do stuff.
[#10] [Foreground]Slept for 5 seconds. Now finishing up the doing of stuff.
The thread 0x2954 has exited with code 0 (0x0).
The program '[11224] KnowAboutDyingThread.vshost.exe' has exited with code 0 (0x0).

这是我的代码:

using System;
using System.Diagnostics;
using System.Threading;

namespace KnowAboutDyingThread
{
    // Source: https://msdn.microsoft.com/en-us/library/h339syd0%28v=vs.110%29.aspx
    // When the runtime stops a background thread because the process is shutting down, 
    // no exception is thrown in the thread. However, when threads are stopped because the 
    // AppDomain.Unload method unloads the application domain, a ThreadAbortException is 
    // thrown in both foreground and background threads.

    // I am trying that out
    // I asked a question here about it: http://stackoverflow.com/questions/37552668/is-there-a-way-to-know-if-a-thread-is-being-aborted-because-the-program-is-termi
    // Permalink: http://stackoverflow.com/q/37552668/303685

    class Program
    {
        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.DomainUnload += CurrentDomain_DomainUnload;
            ThreadPool.QueueUserWorkItem(Do);
            var t = new Thread(Do);
            t.Name = "Foreground";
            t.Start();

            Process.GetCurrentProcess().Close();
        }

        private static void CurrentDomain_DomainUnload(object sender, EventArgs e)
        {
            Debug.Print("Unloading current appdomain...");
        }

        static void Do(object state)
        {
            try
            {
                Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Starting to do stuff."));
                Thread.Sleep(5000);
                Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Slept for 5 seconds. Now finishing up the doing of stuff."));
            }
            catch(ThreadAbortException abort)
            {
                Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Do got ThreadAbortException: {abort.GetType().Name}: {abort.Message}"));
            }
            catch(Exception ex)
            {
                Debug.Print(string.Format($"[#{Thread.CurrentThread.ManagedThreadId}] [{Thread.CurrentThread.Name ?? "Worker"}]Do had an exception: {ex.GetType().Name}: {ex.Message}"));
            }
        }
    }
}





  [1]: https://msdn.microsoft.com/en-us/library/system.appdomain.domainunload%28v=vs.110%29.aspx

1 个答案:

答案 0 :(得分:0)

后台线程不应该正常终止。

您将无法捕获任何异步异常。这也适用于前景线程。

在前台线程中,您将能够优雅地进入public class AnotherClass { public AnotherClass(UniteConverter ac) { ac.input.addTextChangedListener(new TextWatcher() { public void afterTextChanged(Editable s) { } public void beforeTextChanged(CharSequence s, int start, int count, int after) { } public void onTextChanged(CharSequence s, int start, int before, int count) { } } } 块,但不能进入catch块。

您可以做的最简单的事情可能是使用最终对象:

finally

输出:

using System;
using System.Diagnostics;
using System.Threading;

namespace ReliableStop
{
    class Program
    {
        static void Main(string[] args)
        {
            Debug.WriteLine("-- Started Main. Thread: {0}", Thread.CurrentThread.ManagedThreadId);

            var everStarted = new ManualResetEvent(false);

            var t = new Thread(o =>
            {
                Debug.WriteLine("-- Thread entered here.");
                everStarted.Set();

                using (new PostMortemThreadDump())
                {
                    try
                    {
                        Thread.Sleep(100);
                    }
                    catch
                    {
                        Debug.WriteLine("-- Attempt to catch everything.");
                    }
                    finally
                    {
                        Debug.WriteLine("-- Attempt to process finally.");
                    }                    
                }
            });
            t.IsBackground = true;
            t.Start();

            // Check that the thread has started.
            everStarted.WaitOne();

            // ... Good bye, no need to kill, I will die on my own.
        }
    }

    class PostMortemThreadDump : IDisposable
    {
        int threadId { get; }
        public PostMortemThreadDump()
        {
            threadId = Thread.CurrentThread.ManagedThreadId;
        }

        ~PostMortemThreadDump()
        {
            Dispose(false);
        }

        void Dispose(bool disposing)
        {
            if (disposing)
            {
                Debug.WriteLine("-- PostMortemThreadDump. Finished normally.");
            }
            else
            {
                Debug.WriteLine("-- PostMortemThreadDump. Thread: {0}", threadId);
            }
        }

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

现在,如果你改为使用前台线程,你也会得到`finally块执行:

-- Started Main. Thread: 9
-- Thread entered here.
The thread 0x6fc8 has exited with code 0 (0x0).
The thread 0x12b4 has exited with code 0 (0x0).
-- PostMortemThreadDump. Thread: 10
The program '[25260] ReliableStop.vshost.exe' has exited with code 0 (0x0).

正如您所看到的,终结器在两种情况下运行,并且在前台线程中,您将执行-- Started Main. Thread: 9 -- Thread entered here. The thread 0x4d54 has exited with code 0 (0x0). The thread 0x20c8 has exited with code 0 (0x0). -- Attempt to process finally. -- PostMortemThreadDump. Finished normally. The thread 0x6928 has exited with code 0 (0x0). The program '[26328] ReliableStop.vshost.exe' has exited with code 0 (0x0). 块。

调用finally不会影响此方案。