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



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

我需要这个为每个线程关闭连同线程Id的信息。

我本来希望这将是AppDomainUnload事件,但文件上说没有。

MSDN上的这个页面上写着:

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

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

这不仅应该调用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

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

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

在前台线程中,您将能够优雅地进入finally块,但不能进入catch块。

你能做的最简单的事情可能是使用最终确定的对象:

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);
}
}
}

输出:

-- 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).

现在,如果你改为使用前台线程,你也会得到"finally block executed:">

-- 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块。

调用Process.GetCurrentProcess().Close()不会影响此场景。

最新更新